BZOJ 3529 [Sdoi2014]数表 (莫比乌斯反演+树状数组+离线)

题目大意:有一张$n*m$的数表,第$i$行第$j$列的数是同时能整除$i,j$的所有数之和,求数表内所有不大于A的数之和

先是看错题了...接着看对题了发现不会做了...刚了大半个下午无果

看了Po姐的题解(Orzzz)才搞懂这道题,搞清楚了莫比乌斯反演的两种经典的卷积形式的不同之处

令$\sigma(i)$表示i的约数和

如果去掉A这个限制,则题目是让我们求$\sum_{i=1}^{n}\sum_{j=1}^{m}\sigma(gcd(i,j))$

考虑如何正确转化式子,让我们能够把不大于A这个限制加上去,再用某种数据结构快速维护,否则我们转化的式子就是失败的

令$f(i)$表示$i$作为$gcd$出现的次数,利用莫比乌斯反演的常规套路,可得$f(i)=\sum_{i|d}\mu(d/i)\left \lfloor \frac{n}{d} \right \rfloor\left \lfloor \frac{m}{d} \right \rfloor$

这样,再接下来的变形中,可以把d提到前面去,我们就能对d的范围进行限制了

那么结果$ans=\sum f(i)\sigma(i)$

展开得$ans=\sum_{i=1}^{n} \sigma(i)\sum_{i|d}\mu(d/i)\left \lfloor \frac{n}{d} \right \rfloor\left \lfloor \frac{m}{d} \right \rfloor$

移项$ans=\sum _{d=1}^{\sigma(d)<=A} \left \lfloor \frac{n}{d} \right \rfloor\left \lfloor \frac{m}{d} \right \rfloor \sum_{i|d}\mu(d/i)\sigma(i)$

对于前面的部分,对询问离线,按A从小到大排序,从小到大遍历即可$\sum _{d=1}^{\sigma(d)<=A} \left \lfloor \frac{n}{d} \right \rfloor\left \lfloor \frac{m}{d} \right \rfloor$

对于后面的部分$\sum_{i|d}\mu(d/i)\sigma(i)$当前询问的A大于等于$\sigma(i)$时,我们就要把i的贡献加进去,即所有能整除$i$的数的位置都要加上$\mu(d/i)\sigma(i)$

因为$\sigma(i)$并不具有单调性,所以需要用树状数组维护前缀和,可以暴力往树状数组里插入,因为总插入次数是$O(nlnn)$(调和级数)

接着对于每个询问,利用整除分块的思想求解即可

总时间$O(nlog^{2}n+Q\sqrt{n}logn)$,有点卡常,建议用unsigned int 

 1 #include <cstdio>
 2 #include <cstring>
 3 #include <algorithm>
 4 #define N 100100
 5 #define maxn 100000
 6 #define ll long long 
 7 #define uint unsigned int
 8 using namespace std;
 9 
10 const uint mod=2147483648ll;
11 int T,cnt;
12 int use[N],pr[N],mu[N],nxt[N];
13 uint sum[N];
14 ll sgm[N];
15 struct node{ll s;int id;}a[N];
16 int cmp1(node s1,node s2){return s1.s<s2.s;}
17 struct Ques{int n,m,A,id;ll ans;}Q[N];
18 int cmp2(Ques s1,Ques s2){return s1.A<s2.A;}
19 int cmp3(Ques s1,Ques s2){return s1.id<s2.id;}
20 
21 void update(int x,uint w){for(int i=x;i<=maxn;i+=(i&(-i)))(sum[i]+=w)%=mod;}
22 uint query(int x){uint ans=0;for(int i=x;i>0;i-=(i&(-i)))(ans+=sum[i])%=mod;return ans;}
23 void Pre()
24 {
25     mu[1]=1;
26     for(int i=2;i<=maxn;i++)
27     {
28         if(!use[i]) pr[++cnt]=i,mu[i]=-1,nxt[i]=i;
29         for(int j=1;j<=cnt&&i*pr[j]<=maxn;j++){
30             use[i*pr[j]]=1;
31             nxt[i*pr[j]]=pr[j];
32             if(i%pr[j]==0){
33                 mu[i*pr[j]]=0;
34                 break;
35             }else{
36                 mu[i*pr[j]]=-mu[i];
37             }
38         }
39     }
40     for(int i=1;i<=maxn;i++)
41         for(int j=i;j<=maxn;j+=i)
42             sgm[j]+=i;
43     for(int i=1;i<=maxn;i++)
44         a[i].s=sgm[i],a[i].id=i;
45     sort(a+1,a+maxn+1,cmp1);
46 }
47 
48 int main()
49 {
50     //freopen("t1.in","r",stdin);
51     scanf("%d",&T);
52     Pre();
53     int x,y,z;
54     for(int t=1;t<=T;t++)
55     {
56         scanf("%d%d%d",&Q[t].n,&Q[t].m,&Q[t].A);
57         Q[t].id=t;
58     }
59     sort(Q+1,Q+T+1,cmp2);
60     int k=1;
61     for(int t=1;t<=T;t++)
62     {
63         while(k<=maxn&&a[k].s<=Q[t].A){
64             int i=a[k].id;
65             for(int j=i;j<=maxn;j+=i)
66                 update(j,sgm[i]*mu[j/i]%mod);
67             k++;}
68         if(Q[t].n>Q[t].m) swap(Q[t].n,Q[t].m);
69         int n=Q[t].n,m=Q[t].m;ll ans=0;
70         for(int j=1,la;j<=n;j=la+1){
71             la=min(n/(n/j),m/(m/j));
72             (ans+=1ll*(n/j)*(m/j)%mod*(query(la)-query(j-1))%mod)%=mod;
73         }Q[t].ans=(1ll*ans%mod+mod)%mod;
74     }
75     sort(Q+1,Q+T+1,cmp3);
76     for(int t=1;t<=T;t++)
77         printf("%lld\n",Q[t].ans);
78     return 0;
79 }

 

posted @ 2018-11-17 23:30  guapisolo  阅读(149)  评论(0编辑  收藏  举报