【BZOJ3529】數表
阿新 • • 發佈:2018-11-21
數表
Description
有一張 n*m 的數表,其第i行第j列(1<=i<=n,1<=j<=m)的數值為能同時整除 i和j的所有自然數之和。給定a,計算數表中不大於a的數之和。
Input
輸入包含多組資料。
輸入的第一行一個整數Q,表示測試點內的資料組數;
接下來Q行,每行三個整數n,m,a(|a|<=10^9 )描述一組資料。
Output
對每組資料,輸出一行一個整數,表示答案模2^31的值。
Sample Input
2
4 4 3
10 10 5
Sample Output
20
148
Hint
不妨設\(n<m\)
同時整除\(i,j\)的自然數之和就是\(gcd(i,j)\)的約數之和。我們設\(f(i)=\sum_{d|i}d\)。
則:
\[ \displaystyle ans=\sum_{g=1}^{n}f(g)\sum_{i=1}^{\lfloor \frac{n}{g} \rfloor}\sum_{j=1}^{\lfloor \frac{m}{g} \rfloor}[gcd(i,j)=1]\\ =\sum_{g=1}^{n}f(g)\sum_{i=1}^{\lfloor \frac{n}{g} \rfloor}\sum_{j=1}^{\lfloor \frac{m}{g} \rfloor}\sum_{d|i,d|j}\mu(d) \]
又來套路一波:設\(T=gd\),\(\displaystyle ans=\sum_{T=1}^{n}\sum_{d|T}\mu(d)f(\frac{n}{d})\lfloor \frac{n}{T} \rfloor\lfloor \frac{m}{T} \rfloor\)。
然後又了a的限制後,我們就將詢問和\(f\)都離線下來排序,加入樹狀數組裡面。
程式碼:
#include<iostream> #include<cstdio> #include<cstring> #include<cmath> #include<complex> #include<algorithm> #include<set> #include<map> #include<vector> #include<ctime> #include<queue> #include<iomanip> #define ll long long #define N 100005 #define int ll using namespace std; inline int Get() {int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9') {if(ch=='-') f=-1;ch=getchar();}while('0'<=ch&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0';ch=getchar();}return x*f;} int Q; int pri[N]; bool vis[N]; ll sum[N]; int mu[N]; struct node { int id; ll sum; bool operator <(const node &a)const { return sum<a.sum; } }st[N]; int cnt; struct query { int n,m,id; ll a; bool operator <(const query &x)const {return a<x.a;} }q[20005]; void pre(int n) { mu[1]=1; for(int i=2;i<=n;i++) { if(!vis[i]) { pri[++pri[0]]=i; mu[i]=-1; } for(int j=1;j<=pri[0]&&i*pri[j]<=n;j++) { vis[i*pri[j]]=1; if(i%pri[j]==0) { mu[i*pri[j]]=0; break; } mu[i*pri[j]]=-mu[i]; } } for(int i=1;i<=n;i++) { for(int j=i;j<=n;j+=i) { sum[j]+=i; } } cnt=n; for(int i=1;i<=n;i++) st[i]=(node) {i,sum[i]}; } ll tem[N]; int low(int i) {return i&(-i);} const ll mod=(1ll<<31); void add(int v,ll f) {for(int i=v;i<=100000;i+=low(i)) (tem[i]+=f)%=mod;} void update(int v) { for(int i=v;i<=100000;i+=v) { if(!mu[i/v]) continue ; add(i,(sum[v]*mu[i/v]%mod+mod)%mod); } } ll Ask(int v) { ll ans=0; for(int i=v;i;i-=low(i)) (ans+=tem[i])%=mod; return ans; } ll Ask(int l,int r) {return (Ask(r)-Ask(l-1)+mod)%mod;} ll ans[20005]; int now; ll solve(int n,int m) { if(n>m) swap(n,m); int last; ll ans=0; for(int i=1;i<=n;i=last+1) { last=min(n/(n/i),m/(m/i)); (ans+=1ll*(n/i)*(m/i)%mod*Ask(i,last)%mod)%=mod; } return ans; } signed main() { pre(100000); sort(st+1,st+1+cnt); Q=Get(); for(int i=1;i<=Q;i++) { q[i].n=Get(),q[i].m=Get(),q[i].a=Get(); q[i].id=i; } sort(q+1,q+1+Q); int tag=1; for(int i=1;i<=Q;i++) { while(tag<=cnt&&st[tag].sum<=q[i].a) { update(st[tag].id); tag++; } now=i; ans[q[i].id]=solve(q[i].n,q[i].m); } for(int i=1;i<=Q;i++) cout<<ans[i]<<"\n"; return 0; }