【BZOJ3529】数表

数表

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

img

不妨设\(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;
}
posted @ 2018-11-21 17:27  hec0411  阅读(173)  评论(0编辑  收藏  举报