数表 bzoj-3529 Sdoi-2014
题目大意:n*m的数表,第i行第j列的数是同时整除i和j的所有自然数之和。给定a,求数表中所有不超过a的和。
注释:$1le n,m le 10^5$。
想法:我们先不考虑那个a的限制:我们设f(i)表示整除i的自然数之和。
$sumlimits_{i=1}^nsumlimits_{j=1}^m f(gcd(i,j))$
$=sumlimits_{i=1}^nsumlimits_{j=1}^m f(d)cdot [gcd(i,j)==d]$
$=sumlimits_{d=1}^n f(d)sumlimits_{i=1}^{lfloorfrac{n}{d} floor}sumlimits_{j=1}^{lfloorfrac{m}{d} floor}[gcd(i,j)==1]$
$=sumlimits_{d=1}^n f(d)sumlimits_{i=1}^{lfloorfrac{n}{d} floor}sumlimits_{j=1}^{lfloorfrac{m}{d} floor}sumlimits_{e|i,e|j} mu(e)$
$=sumlimits_{d=1}^n f(d)sumlimits_{e=1}^{lfloorfrac{n}{d} floor}mu(e)sumlimits_{i=1}^{lfloorfrac{n}{de} floor}sumlimits_{j=1}^{lfloorfrac{m}{de} floor}$
$=sumlimits_{D=1}^n sumlimits_{d|D} f(d)cdot mu(frac{D}{d})sum(lfloorfrac{n}{D} floor)sum(lfloorfrac{m}{D} floor)$
然后,显然$f$函数是积性函数,$mu$函数是积性函数,所以$f$和$mu$的狄利克雷卷积$fcdot 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 ",ans[i]); return 0; }
小结:这就是典型的拟对象的题,我们通过先构造拟对象,然后向完全对象转化,非常巧妙。