UOJ Logo zhouzixuan的博客

博客

bzoj 3739: DZY loves math VIII

2015-12-30 16:16:11 By zhouzixuan
【题目描述】:
    在XYZ的dzy loves math6问世后,dzy一直觉得这道题答案太大,一点都不优美,于是他随手在外面套上一个μ。同时,他又觉得输入两个数实在太麻烦,于是题目变成了:

    你能解决这个问题吗?
【题目解法】:
    首先我们知道只有不含有平方因子的数mu才不为0
    因此gcd(i,j)=1(不会html别打我TAT...)
    所有ans=sigma(mu(ij)*sigma(mu[d]){d|i d|j})=sigma(mu(i)*sigma(mu(d)*g(i,d){d|i}))
    其中g(i,d)=sigma(mu(d)+mu(2d)+...+mu(n))
    因此我们可以枚举i,然后计算此时的答案与ans[i-1]累加即为ans[i]
    首先我们知道mu(i)!=0,因此i不含有平方因子,然后把i分解质因数最多只有log级别个大概是20+
    因此我们可以暴力枚举i的因子,然后计算g
    我们知道g(i-d,d)的答案已经知道,g(i,d)=g(i-d,d)+mu(i)
    因此每次更新g即可,然后根据上面的式子计算即可
    然而我发现分解质因数的方法太慢了!!!
    然后去%claris的程序,发现我们可以预处理出minnum[i]表示i的最小质因子
    然后就可以在log的时间下求出所有的质因子,而且速度很快!%%%claris
#include <cstdio>
#include <cstdlib>
#include <cstring>
#include <algorithm>
#include <cmath>
using namespace std;
const int N=10000005;
int n,m,prime[N],mobius[N],tot,ans[N],minnum[N];
bool v[N]; int g[N],delta,cnt,p[N],nowans,a[N];
inline long long getint()
{
    long long x=0; char c=getchar(); bool flag=false;
    while ((c!='-')&&((c<'0')||(c>'9'))) c=getchar();
    if (c=='-') flag=true,c=getchar();
    while ((c>='0')&&(c<='9')) x=x*10+(long long)(c-'0'),c=getchar();
    if (flag) return -x; else return x;
}
void init()
{
    m=getint();
    for (int i=1; i<=m; i++) a[i]=getint(),n=max(n,a[i]);
    memset(v,true,sizeof(v)); mobius[1]=1;
    for (int i=2; i<=n; i++)
    {
        if (v[i]) prime[++tot]=i,mobius[i]=-1,minnum[i]=i;
        for (int j=1; j<=tot; j++)
        {
            if ((long long)i*prime[j]>n) break;
            v[i*prime[j]]=false; minnum[i*prime[j]]=prime[j];
            mobius[i*prime[j]]=-mobius[i];
            if (i%prime[j]==0) {mobius[i*prime[j]]=0; break;}
        }
    }
}
void dfs(int x,int val)
{
    if (x>cnt)
    {
        g[val]+=delta;
        nowans+=mobius[val]*g[val];
        return;
    }
    dfs(x+1,val); dfs(x+1,val*p[x]);
}
void build()
{
    for (int i=1; i<=n; i++)
    {
        ans[i]=ans[i-1]; if (mobius[i]==0) continue; cnt=0;
        for (int x=i;x>1;x/=minnum[x]) p[++cnt]=minnum[x];
        delta=mobius[i]; nowans=0; dfs(1,1);
        ans[i]+=delta*nowans;
    }
}
void solve()
{
    for (int i=1; i<=m; i++) printf("%d\n",ans[a[i]]);
}
int main()
{
    init();
    build();
    solve();
    return 0;
}

评论

暂无评论

发表评论

可以用@mike来提到mike这个用户,mike会被高亮显示。如果你真的想打“@”这个字符,请用“@@”。