[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;
}
posted @ 2018-12-03 20:31  Hyscere  阅读(151)  评论(0编辑  收藏  举报