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
solution
前置知识:莫比乌斯反演
首先忽略(a)这个条件,题目让求的是:
[ans=sum_{i=1}^nsum_{j=1}^mf(gcd(i,j))
]
其中,(f(x))表示(x)的约数和。
然后,我们可以莫比乌斯反演一波,得到:
[ans=sum_{T=1}^{min(n,m)}lfloorfrac{n}{T}
floorlfloorfrac{m}{T}
floorsum_{d|T}f(d)mu(frac{T}{d})
]
然后把后面那块设为(g),即:
[g(n)=sum_{d|n}f(d)mu(frac{n}{d})
]
如果没有a的限制,随便搞搞这题就做完了。
然后很显然可以发现,当(f(d)leqslant a)时,(f(d))才会对(g(n))有贡献。
考虑离线,对读入按(a)排序,然后从小到大更新(g)。
由于数论分块的时候需要的是前缀和,所以可以考虑拿个数据结构维护下,这里树状数组就是个很好的选择。
然后其他的函数线筛或者大力算一下都行。
时间复杂度:(O(n+n*log^2(n)+q*sqrt{n}*log(n)))
#include<bits/stdc++.h>
using namespace std;
#define int unsigned int
void read(int &x) {
x=0;int f=1;char ch=getchar();
for(;!isdigit(ch);ch=getchar()) if(ch=='-') f=-f;
for(;isdigit(ch);ch=getchar()) x=x*10+ch-'0';x*=f;
}
void print(int x) {
if(x<0) x=-x,putchar('-');
if(!x) return ;print(x/10),putchar(x%10+48);
}
void write(int x) {if(!x) putchar('0');else print(x);putchar('
');}
const int maxn = 1e5+1;
int f[maxn],mu[maxn],pri[maxn],vis[maxn],tot,p[maxn],s[maxn];
void sieve() {
f[1]=mu[1]=1;
for(int i=2;i<maxn;i++) {
if(!vis[i]) pri[++tot]=i,mu[i]=-1,f[i]=i+1,p[i]=i,s[i]=i+1;
for(int t,j=1;j<=tot&&i*pri[j]<maxn;j++) {
vis[t=i*pri[j]]=1;
if(i%pri[j]==0) {
mu[t]=0;p[t]=p[i]*pri[j],s[t]=s[i]+p[t];
f[t]=f[i]/s[i]*s[t];break;
}
s[t]=pri[j]+1,f[t]=f[i]*s[t],p[t]=pri[j],mu[t]=-mu[i];
}
}
//for(int i=1;i<=10;i++) printf("%d %d %d %d
",i,f[i],s[i],p[i]);;
}
struct Binary_Indexed_Tree {
int tr[maxn];
void add(int x,int v) {for(int i=x;i<maxn;i+=i&-i) tr[i]+=v;}
int query(int x,int ans=0) {for(int i=x;i;i-=i&-i) ans+=tr[i];return ans;}
}BIT;
int solve(int n,int m) {
int T=1,ans=0;
while(T<=n) {
int pre=T;T=min(n/(n/T),m/(m/T));
ans+=(n/T)*(m/T)*(BIT.query(T)-BIT.query(pre-1));T++;
}return ans;
}
int n;
struct input {
int n,m,a,id;
int operator < (const input &rhs ) const {return a<rhs.a;}
}in[maxn],ans[maxn];
struct Pair {
int first,second;
int operator < (const Pair &rhs ) const {return first<rhs.first;}
}g[maxn];
int cmp(input a,input b) {return a.id<b.id;}
signed main() {
sieve();read(n);
for(int i=1;i<=n;i++) read(in[i].n),read(in[i].m),read(in[i].a),in[i].id=i;
sort(in+1,in+n+1);for(int i=1;i<maxn;i++) g[i].first=f[i],g[i].second=i;
sort(g+1,g+maxn);int now=0;
for(int i=1;i<=n;i++) {
while(g[now+1].first<=in[i].a&&now+1<maxn) {
now++;
for(int i=g[now].second;i<maxn;i+=g[now].second)
BIT.add(i,g[now].first*mu[i/g[now].second]);
}
if(in[i].n>in[i].m) swap(in[i].n,in[i].m);
ans[i].a=solve(in[i].n,in[i].m),ans[i].id=in[i].id;
}
sort(ans+1,ans+n+1,cmp);
for(int i=1;i<=n;i++) write(ans[i].a&((1u<<31)-1));
return 0;
}