[bzoj3529][Sdoi2014]數表_樹狀數組_莫比烏斯反演
數表 bzoj-3529 Sdoi-2014
題目大意:n*m的數表,第i行第j列的數是同時整除i和j的所有自然數之和。給定a,求數表中所有不超過a的和。
註釋:$1\le n,m \le 10^5$。
想法:我們先不考慮那個a的限制:我們設f(i)表示整除i的自然數之和。
$\sum\limits_{i=1}^n\sum\limits_{j=1}^m f(gcd(i,j))$
$\sum\limits_{i=1}^n\sum\limits_{j=1}^m f(d)\cdot [gcd(i,j)==d]$
$\sum\limits_{d=1}^n f(d)\sum\limits_{i=1}^{\lfloor\frac{n}{d}\rfloor}\sum\limits_{j=1}^{\lfloor\frac{m}{d}\rfloor}[gcd(i,j)==1]$
$\sum\limits_{d=1}^n f(d)\sum\limits_{i=1}^{\lfloor\frac{n}{d}\rfloor}\sum\limits_{j=1}^{\lfloor\frac{m}{d}\rfloor}\sum\limits_{e|i,e|j} \mu(e)$
$\sum\limits_{d=1}^n f(d)\sum\limits_{e=1}^{\lfloor\frac{n}{d}\rfloor}\mu(e)\sum\limits_{i=1}^{\lfloor\frac{n}{de}\rfloor}\sum\limits_{j=1}^{\lfloor\frac{m}{de}\rfloor}$
$\sum\limits_{D=1}^n \sum\limits_{d|D} f(d)\cdot \mu(\frac{D}{d})sum(\lfloor\frac{n}{D}\rfloor)sum(\lfloor\frac{m}{D}\rfloor)$
然後,顯然$f$函數是積性函數,$\mu$函數是積性函數,所以$f$和$\mu$的狄利克雷卷積$f\cdot \mu$是積性函數,所以不限制的問題就解決了。
那我們考慮限制怎麽辦?其實也非常簡單。我們只需要在樹狀數組上維護出小於a的f,查詢即可。
最後,附上醜陋的代碼... ...
#include <iostream> #include <cstdio> #include <cstring> #include <algorithm> #define inf 2147483647 using namespace std; typedef long long ll; const int N=100010; int mu[N],e[N],ans[N],c[N],vis[N],p[N],t[N],g[N]; struct F{int d,num;}f[N]; struct Q{int n,m,a,id;}q[N]; inline bool cmpT(Q a,Q b){return a.a<b.a;} inline bool cmpt(F a,F b){return a.d<b.d;} inline int lowbit(int x){return x&-x;} int power(int a,int b) { int res=1; while(b) { if (b&1) res*=a; a*=a; b>>=1; } return res; } void add(int x,int val) { for(int i=x;i<N;i+=lowbit(i)) c[i]+=val; } int query(int x) { int s=0; for(int i=x;i;i-=lowbit(i)) s+=c[i]; return s; } int main() { mu[1]=1;f[1].d=f[1].num=1; for(int i=2;i<N;i++) { f[i].num=i; if(!vis[i]) mu[i]=-1,f[i].d=t[i]=1+i,g[i]=1,p[++p[0]]=i; for(int j=1;j<=p[0] && i*p[j]<N;j++) { vis[i*p[j]]=1; if(i%p[j]==0) { mu[i*p[j]]=0; g[i*p[j]]=g[i]+1; t[i*p[j]]=t[i]+power(p[j],g[i]+1); f[i*p[j]].d=f[i].d/t[i]*t[i*p[j]]; break; } else { mu[i*p[j]]=-mu[i]; f[i*p[j]].d=f[i].d*f[p[j]].d; g[i*p[j]]=1;t[i*p[j]]=p[j]+1; } } } int T; scanf("%d",&T); for(int i=1;i<=T;i++) scanf("%d%d%d",&q[i].n,&q[i].m,&q[i].a),q[i].id=i; sort(q+1,q+1+T,cmpT); sort(f+1,f+N,cmpt); for(int now=0,i=1;i<=T;i++) { while(now+1<N && f[now+1].d<=q[i].a) { now++; for(int j=1;j*f[now].num<N;j++) { add(j*f[now].num,mu[j]*f[now].d); } } int n=q[i].n,m=q[i].m; if(n>m) swap(n,m); for(int j=1,k;j<=n;j=k+1) { k=min(n/(n/j),m/(m/j)); ans[q[i].id]+=(n/j)*(m/j)*(query(k)-query(j-1)); } ans[q[i].id]&=inf; } for(int i=1;i<=T;i++) printf("%d\n",ans[i]); return 0; }
小結:這就是典型的擬對象的題,我們通過先構造擬對象,然後向完全對象轉化,非常巧妙。
[bzoj3529][Sdoi2014]數表_樹狀數組_莫比烏斯反演