【BZOJ3529】数表(莫比乌斯反演)(树状数组)(数学)

来源:互联网 发布:苹果6为什么没有4g网络 编辑:程序博客网 时间:2024/06/06 04:35

题目大意:

1in,1jmgcd(i,j)的因数之和中不大于a的值之和,即:

i=1nj=1md|gcd(i,j)d)×d|gcd(i,j)da

题解:

假设没有a的限制。
f(n,m,i)1xn,1ym中,gcd(x,y)=i的个数。
F(n,m,i)1xn,1ym中,i|gcd(x,y)的个数。

f(n,m,i)=i|dμ(di)F(n,m,d)=i|dμ(di)ndmd

σ(i)i的因数之和。
i=1min(n,m)σ(i)f(n,m,i)=i=1min(n,m)σ(i)i|dμ(di)ndmd

=d=1min(n,m)ndmdi|dμ(di)σ(i)

暴力处理前缀和i|dμ(di)σ(i),即枚举i,处理i的倍数。
计算σ(i)也是靠暴力,枚举i,处理i的倍数(保证不超时)。

现在来考虑有a的限制。
发现只有σ(i)a才会加进去,则将询问按a从小到大排序,σ(i)也从小到大排序,用树状数组记录前缀和,每次将σ(i)ai|dμ(di)σ(i)加入前缀和,就可以做了。

代码:

#include<cstdio>#include<algorithm>using namespace std;const int MAXN=100005,MAXQ=20005;typedef pair<int,int> sigma;struct Query{    int n,m,a,id;    bool operator < (const Query &t)const    {        return a<t.a;    }};struct tsum{    int s[MAXN];    void add(int val,int id)    {        for(int i=id;i<MAXN;i+=(i&(-i)))            s[i]+=val;    }    int get(int id)    {        int ret=0;        for(int i=id;i>0;i-=(i&(-i)))            ret+=s[i];        return ret;    }};Query que[MAXQ];int ans[MAXQ];sigma si[MAXN];int mu[MAXN],prime[MAXN/3],pcnt;bool noprime[MAXN];tsum fu;void Init();int main(){    Init();    int Q;    scanf("%d",&Q);    for(int i=1;i<=Q;i++)    {        scanf("%d%d%d",&que[i].n,&que[i].m,&que[i].a);        que[i].id=i;    }    sort(que+1,que+Q+1);    int lasta=0;    for(int i=1;i<=Q;i++)    {        for(int j=lasta+1;si[j].first<=que[i].a;j++)        {            for(int k=1;k*si[j].second<MAXN;k++)                fu.add(si[j].first*mu[k],k*si[j].second);            lasta=j;        }        if(que[i].n>que[i].m)            swap(que[i].n,que[i].m);        int last=0;        for(int d=1;d<=que[i].n;d=last+1)        {            last=min(que[i].n/(que[i].n/d),que[i].m/(que[i].m/d));            ans[que[i].id]+=(que[i].n/d)*(que[i].m/d)*(fu.get(last)-fu.get(d-1));        }        ans[que[i].id]&=0x7FFFFFFF;    }    for(int i=1;i<=Q;i++)        printf("%d\n",ans[i]);    return 0;}void Init(){    noprime[1]=1;    mu[1]=1;    for(int i=1;i<MAXN;i++)    {        if(!noprime[i])        {            prime[++pcnt]=i;            mu[i]=-1;        }        for(int j=1;j<=pcnt&&i*prime[j]<MAXN;j++)        {            noprime[i*prime[j]]=1;            if(i%prime[j]==0)                break;            mu[i*prime[j]]=-mu[i];        }    }    for(int i=1;i<MAXN;i++)        for(int j=i;j<MAXN;j+=i)            si[j].first+=i;    for(int i=1;i<MAXN;i++)        si[i].second=i;    sort(si+1,si+MAXN);}
阅读全文
0 0
原创粉丝点击