[SDOI2014]数表 - 题解

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}^{\lfloor\frac{n}{k}\rfloor}{\mu(d)\lfloor\frac{n}{id}\rfloor\lfloor\frac{m}{id}\rfloor} \]

\[t = id \]

\[\sum_{i=1}^{n}{F(i)G(i)} \]

\[= \sum_{i=1}^{n}{F(i)}\sum_{i|t}{\mu(\frac{t}{i})\lfloor\frac{n}{t}\rfloor\lfloor\frac{m}{t}\rfloor} \]

\[= \sum_{t=1}^{n}\lfloor\frac{n}{t}\rfloor\lfloor\frac{m}{t}\rfloor\sum_{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\n",ans[i]&mod);
	return 0;
}
posted @ 2019-01-15 20:17  daniel14311531  阅读(237)  评论(0编辑  收藏  举报