Loading

P3312 [SDOI2014]数表 题解

P3312 [SDOI2014]数表

推式子的时候默认 \(n < m\)

\(\sigma(i)\) 表示 \(i\) 的约数和

\[ ans= \sum\limits _{i=1}^n\sum\limits_{j=1}^{n} \sigma(\gcd(i,j))\\ =\sum\limits_{d=1}^{n} \sum \limits_{i=1}^{\frac{n}{d}} \sum \limits_{j=1}^{\frac{m}{d}} \sigma(d)[\gcd(i,j)==1]\\ =\sum\limits_{d=1}^{n} \sum \limits_{i=1}^{\frac{n}{d}} \sum \limits_{j=1}^{\frac{m}{d}} \sum\limits_{x|d}\mu(x) \sigma(d)\\ =\sum\limits_{d=1}^{n} \sigma(d) \sum \limits_{i=1}^{\frac{n}{d}} \sum \limits_{j=1}^{\frac{m}{d}} \sum\limits_{x|d}\mu(x)\\ =\sum\limits_{d=1}^{n} \sigma(d) \sum\limits_{x=1}^{\frac{n}{d}}\mu(x) \dfrac{n}{dx} \dfrac{m}{dx}\\ \sum\limits_{d=1}^n \sigma(d) \sum\limits_{T=1}^{n} \mu(\dfrac{T}{d})\dfrac{n}{T} \dfrac{m}{T}(T=dx)\\ =\sum\limits_{T=1}^{n}\dfrac{n}{T} \dfrac{m}{T}\sum\limits_{d|T}\sigma(d)\mu(\dfrac{T}{d})\\ \]

先线性筛出 \(\mu\)\(\sigma\) 可以 \(O(n\log n)\) 暴力算,前面套个整除分块即可。

但是加上 \(a\) 的限制之后。。。

发现只有 \(\sigma(i)\le a\) 的时候才可以对于那个 \(T\) 产生贡献。

所以考虑离线询问,将 \(a\) 从小到大排序,还要预处理所有的 \(\sigma(i)\) 从小到大排序,用树状数组暴力修改即可。

附赠一组较强样数据供调试

Input
1
25291 26958 42646
Output
235628669
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
typedef double db;
#define pb(x) push_back(x)
#define mkp(x,y) make_pair(x,y)
//#define getchar() (p1==p2&&(p2=(p1=buf)+fread(buf,1,1<<21,stdin),p1==p2)?EOF:*p1++)
//char buf[1<<21],*p1=buf,*p2=buf;
#define int long long
inline unsigned read() {
	unsigned x=0,f=1;char ch=getchar();
	while(!isdigit(ch)) {if(ch=='-')f=-1;ch=getchar();}
	while(isdigit(ch))x=x*10+(ch^48),ch=getchar();
	return x*f;
}
const int N = 20005;
const int M = 100005;
const int mod = 1ll << 31;
int ans[N], tr[M];
bool vis[M];
int mu[M], pri[M], cnt, q, it = 1;
struct node {
	int n, m, a, id;
	inline bool operator<(const node&rhs)const{return a < rhs.a;}
}a[N];
struct fact {
	int id, x;
	inline bool operator<(const fact&rhs)const{return x < rhs.x;}
}f[M];
void add(int x, int d) {
	for (int i = x; i < M; i += i & -i) tr[i] += d;
}
int ask(int x) {
	int res = 0;
	for (int i = x; i > 0; i -= i & -i) res += tr[i];
	return res;
}
void Sieve() {
	mu[1] = 1;
	for (int i = 2; i < M; ++ i) {
		if (! vis[i]) pri[++ cnt] = i, mu[i] = -1;
		for (int j = 1; i * pri[j] < M; ++ j) {
			vis[i * pri[j]] = 1;
			if(i % pri[j] == 0) break;
			mu[i * pri[j]] = - mu[i];
		}
	}
	for (int i = 1; i < M; ++ i) 
		for (int j = 1; i * j < M; ++ j)
			f[i * j].x += i;
	for(int i = 1; i < M; ++ i) f[i].id = i;
	sort(f + 1, f + M);
}
signed main() {
	Sieve();
	q = read();
	for (int i = 1; i <= q; ++ i)
		a[i].n = read(), a[i].m = read(), a[i].a = read(), a[i].id = i;
	sort(a + 1, a + q + 1);
	for (int i = 1; i <= q; ++ i) {
		while (it < M && f[it].x <= a[i].a) {
			int t = f[it].id;
			for (int j = 1; j * t < M; ++ j) add(j * t, f[it].x * mu[j]);
			++ it;
		} 
		int res = 0, n = a[i].n, m = a[i].m;
		if (n > m) swap(n, m);
		for (int l = 1, r; l <= n; l = r + 1)
			r = min(n / (n / l), m / (m / l)), res += (n / l) * (m / l) * (ask(r) - ask(l - 1));
		ans[a[i].id] = res;
	}
	for (int i = 1; i <= q; ++ i) printf("%lld\n", ans[i] % mod);
	return 0;
}
posted @ 2020-09-25 13:03  zzctommy  阅读(143)  评论(0编辑  收藏  举报