[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
solution
首先忽略\(a\)這個條件,題目讓求的是:
\[ ans=\sum_{i=1}^n\sum_{j=1}^mf(gcd(i,j)) \]
其中,\(f(x)\)表示\(x\)的約數和。
然後,我們可以莫比烏斯反演一波,得到:
\[ ans=\sum_{T=1}^{min(n,m)}\lfloor\frac{n}{T}\rfloor\lfloor\frac{m}{T}\rfloor\sum_{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('\n');} 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\n",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; }