[BZOJ3529] [Sdoi2014]数表

[BZOJ3529] [Sdoi2014]数表

Description

有一张 n×m 的数表,其第 i 行第 j 列(1 <= i <= n, 1 <= j <= m)的数值为能同时整除 i 和 j 的所有自然数之和。给定 a , 计算数表中不大于 a 的数之和。

Input

输入包含多组数据。输入的第一行一个整数Q表示测试点内的数据组数接下来Q行,每行三个整数n,m,a(|a| < =10^9)描述一组数据。1 < =N.m < =10^5 , 1 < =Q < =2×10^4

Output

对每组数据,输出一行一个整数,表示答案模2^31的值。

Sample Input

2
4 4 3
10 10 5

Sample Output

20
148

试题分析

说人话就是求:$$\sum_{i=1}n\sum_{j=1}m \sum_{d|i,d|j}[d\leq a] d$$
条件很烦人,先把它略去好了,转而变成:$$\sum_{i=1}n\sum_{j=1}m f(gcd(i,j))$$
其中\(f(d)\)为d的约数和。
枚举\(gcd(i,j)\)得到:$$\sum_{d=1}^{min(n,m)} f(d) \sum_{i=1}^{\lfloor \frac{n}{d} \rfloor} \sum_{j=1}^{\lfloor \frac{m}{d} \rfloor} [gcd(i,j)=1]$$。
后面的已经是套路,就直接写出:

\[\sum_{d=1}^{min(n,m)} f(d) \sum_{t=1}^{min(\lfloor \frac{n}{d} \rfloor,\lfloor \frac{m}{d} \rfloor)} \mu(t) \lfloor \frac{n}{dt} \rfloor \lfloor \frac{m}{dt} \rfloor$$。 然后令$T=dt$,枚举$T$得到:$${\sum_{T=1}^{min(n,m)}\lfloor\frac{n}{T}\rfloor\lfloor\frac{m}{T}\rfloor\sum_{d|T}f(d)μ(\frac{T}{d})}$$。 至此,基本上大功告成,前面可以用数论分块在$\sqrt{n}$时间内做完,如果加上a的限制直接将$f$排序然后树状数组加入维护前缀和即可。 ```c++ #include<iostream> #include<cstring> #include<cstdio> #include<vector> #include<algorithm> using namespace std; #define LL long long inline int read(){ int x=0,f=1; char c=getchar(); for(;!isdigit(c);c=getchar()) if(c=='-') f=-1; for(;isdigit(c);c=getchar()) x=x*10+c-'0'; return x*f; } const int MAXN = 100010; const int INF = 2147483647; int mu[MAXN+1],g[MAXN+1],c[MAXN+1]; int pri[MAXN+1]; bool vis[MAXN+1]; int N,cnt; struct data{int d,s;}f[MAXN+1]; struct query{int n,m,a,id;}q[MAXN+1]; int t[MAXN+1]; bool cmp(data a,data b){return a.s<b.s;} bool cmp2(query a,query b){return a.a<b.a;} inline int lowbit(int x){return x&(-x);} inline int Query(int x){ int res=0; for(;x;x-=lowbit(x)) res+=c[x]; return res; } inline void Add(int x,int k){ for(;x<=MAXN;x+=lowbit(x)) c[x]+=k; return ; } inline int Pow(int a,int b){ int res=1; while(b){ if(b&1) res=res*a; b>>=1; a=a*a; } return res; } inline void init(){ mu[1]=f[1].d=f[1].s=1; for(int i=2;i<MAXN;i++){ f[i].d=i; if(!vis[i]){mu[i]=-1; f[i].s=t[i]=i+1; g[i]=1; pri[++cnt]=i;} for(int j=1;j<=cnt&&pri[j]*i<MAXN;j++){ vis[i*pri[j]]=true; if(i%pri[j]==0){ mu[i*pri[j]]=0; g[i*pri[j]]=g[i]+1; t[i*pri[j]]=t[i]+Pow(pri[j],g[i]+1); f[i*pri[j]].s=f[i].s/t[i]*t[i*pri[j]]; break; } mu[i*pri[j]]=-mu[i]; f[i*pri[j]].s=f[i].s*f[pri[j]].s; g[i*pri[j]]=1; t[i*pri[j]]=pri[j]+1; } } sort(f+1,f+MAXN,cmp); return ; } int ans[MAXN+1]; int main(){ //freopen(".in","r",stdin); //freopen(".out","w",stdout); N=read(); init(); for(int i=1;i<=N;i++) q[i].n=read(),q[i].m=read(),q[i].a=read(),q[i].id=i; sort(q+1,q+N+1,cmp2); int j=1; for(int i=1;i<=N;i++){ while(f[j].s<=q[i].a&&j<MAXN){ for(int t=1;t*f[j].d<MAXN;t++) Add(f[j].d*t,f[j].s*mu[t]); ++j; } int n=q[i].n,m=q[i].m; if(n>m) swap(n,m); for(int l=1,r;l<=n;l=r+1){ r=min(n/(n/l),m/(m/l)); ans[q[i].id]+=(Query(r)-Query(l-1))*(n/l)*(m/l)%INF; } ans[q[i].id]&=INF; } for(int i=1;i<=N;i++) printf("%d\n",ans[i]); return 0; } ```\]

posted @ 2018-08-28 21:34  wxjor  阅读(187)  评论(0编辑  收藏  举报