SDOI2014 数表
题意:已知
[F(n) = sum_{d|n}{d}
]
求
[sum_{i=1}^{n}sum_{j=1}^{m}{F(gcd(i,j))} mod 2^{31}, F(n) leq a
]
做法:
感谢 acfunction 大佬的题解 给我这个蒟蒻提供思路。
我这只数学辣鸡居然写数学题解。
先忽略 $ a $。
令
[G(k) = sum_{i=1}^{n}sum_{j=1}^{m}[gcd(i,j) == k]
]
则
[G(k) = sum_{d=1}^{lfloorfrac{n}{k}
floor}{mu(d)lfloorfrac{n}{id}
floorlfloorfrac{m}{id}
floor}
]
设
[t = id
]
则
[sum_{i=1}^{n}{F(i)G(i)}
]
[= sum_{i=1}^{n}{F(i)}sum_{i|t}{mu(frac{t}{i})lfloorfrac{n}{t}
floorlfloorfrac{m}{t}
floor}
]
[= sum_{t=1}^{n}lfloorfrac{n}{t}
floorlfloorfrac{m}{t}
floorsum_{i|t}{F(i)mu(frac{t}{i})}
]
将所有询问离线,按 $ a $ 排序,边询问边加值,用树状数组维护前缀和再莫比乌斯反演即可。
#include<bits/stdc++.h>
using namespace std;
typedef unsigned int ui;
const int N=100010,mod=2147483647;
int T;
struct P {
int n,m,a,id;
friend bool operator<(P xx,P yy) { return xx.a<yy.a; }
};P q[N];
struct W {
int x,id;
friend bool operator<(W xx,W yy) { return xx.x<yy.x; }
};W w[N];
int f[N],mu[N],pri[N],lp=0,tr[N],ans[N];
bool p[N];
inline int Min(const int x,const int y) { return x<y? x:y; }
void add(int x,ui w) { for(;x<=N-10;x+=(x&-x)) tr[x]+=w; }
ui ask(int x) { ui sum=0; for(;x;x-=(x&-x)) sum+=tr[x]; return sum; }
void init() {
for(int i=1;i<=N-10;i++) for(int j=i;j<=N-10;j+=i) f[j]+=(ui)i;
mu[1]=1;
for(int i=2;i<=N-10;i++) {
if(!p[i]) pri[++lp]=i,mu[i]=-1;
for(int j=1;pri[j]*i<=N-10;j++) {
p[pri[j]*i]=1; if(i%pri[j]==0) { mu[pri[j]*i]=0;break; }
mu[pri[j]*i]=-mu[i];
}
}
}
ui calc(int n,int m) {
ui sum=0; if(n>m) swap(n,m);
for(int l=1,r;l<=n;l=r+1) {
r=Min(n/(n/l),m/(m/l));
sum=sum+(n/l)*(m/l)*(ask(r)-ask(l-1));
}
return sum;
}
int main() {
init(),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;
for(int i=1;i<=N-10;i++) w[i].x=f[i],w[i].id=i;
sort(q+1,q+T+1),sort(w+1,w+N-10+1);
for(int i=1,j=0;i<=T;i++) {
while(j<N-10&&w[j+1].x<=q[i].a) {
++j;
for(int k=1;k*w[j].id<=N-10;k++)
add(k*w[j].id,w[j].x*mu[k]);
}
ans[q[i].id]=calc(q[i].n,q[i].m);
}
for(int i=1;i<=T;i++) printf("%d
",ans[i]&mod);
return 0;
}