[luogu P1829] [國家集訓隊]Crash的數字表格 / JZPTAB

我丟 : https://www.luogu.com.cn/problem/P1829
n<m先假設n < m
i=1nj=1mlcm(i,j)=i=1nj=1ni×jgcd(i,j)=d=1ni=1n/dj=1m/d[gcd(i,j)=1]d×i×j(d=gcd)=d=1ni=1n/dj=1m/dkgcd(i,j)μ(k)×d×i×j=d=1nk=1n/di=1n/kdj=1m/kdμ(k)×d×ik×jk=T=1nT×i=1n/Ti×j=1m/Tj×kTμ(k)×k(T=kd,kkd=Tk)\begin{aligned} & \sum_{i=1}^n\sum_{j=1}^m lcm(i,j)\\ =& \sum_{i=1}^n\sum_{j=1}^n \frac{i \times j}{\gcd(i,j)} \\ =& \sum_{d=1}^n\sum_{i=1}^{\lfloor n/d \rfloor}\sum_{j=1}^{\lfloor m/d \rfloor}[\gcd(i,j)=1]d\times i \times j (枚舉d=gcd)\\ =& \sum_{d=1}^n\sum_{i=1}^{\lfloor n/d \rfloor}\sum_{j=1}^{\lfloor m/d \rfloor}\sum_{k|\gcd(i,j)}\mu(k) \times d\times i \times j \\ =& \sum_{d=1}^n\sum_{k=1}^{\lfloor n/d\rfloor}\sum_{i=1}^{\lfloor n/kd\rfloor}\sum_{j=1}^{\lfloor m/kd\rfloor}\mu(k)\times d \times ik \times jk \\ =& \sum_{T=1}^{n}T\times\sum_{i=1}^{\lfloor n/T \rfloor}i\times\sum_{j=1}^{\lfloor m/T \rfloor}j\times\sum_{k|T}\mu(k)\times k(設T=kd,k*k*d=T*k) \end{aligned}

i=1n/TikTμ(k)×k\large 顯然\sum\limits_{i=1}^{\lfloor n/T \rfloor}i 和\sum_{k|T}\mu(k)\times k都是可以預處理的
O(n ln n)1.6sTle時間複雜度貌似是 O(n \ ln\ n),理論上最慢要跑1.6s,然鵝常數不允許,還是Tle了
code:

#include<bits/stdc++.h>
#define N 10000005
#define mod 20101009
#define int long long
using namespace std;
int prime[N], vis[N], mu[N], s[N], sz;
void init() {
	mu[1] = 1;
	for(int i = 2; i < N; i ++) {
		if(!vis[i]) {
			prime[++ sz] = i;
			mu[i] = mod - 1;
		}
		for(int j = 1; j <= sz && prime[j] * i < N; j ++) {
			vis[prime[j] * i] = 1;
			if(i % prime[j] == 0) break;
			else mu[i * prime[j]] = mod - mu[i];
		}
	}	
	for(int i = 1; i < N; i ++)
		for(int j = i; j < N; j += i)
			s[j] = (s[j] + mu[i] * i % mod) % mod;
}
int calc(int x) {
	return (x * (x + 1) / 2) % mod;
}
int n, m;
signed main() {
	init();
	scanf("%lld%lld", &n, &m);
	int ans = 0;
	for(int T = 1; T < N; T ++) {
		int ha = 0;
		ans += T * calc(n / T) % mod * calc(m / T) % mod * s[T] % mod, ans %= mod;
	}
	printf("%lld", ans);	
	return 0;
}

主要是倍數那裏好像優化不了了,那就換一種方法推吧,枚舉倍數
i=1nj=1mlcm(i,j)=d=1nk=1n/di=1n/kdj=1m/kdμ(k)×d×ik×jk=d=1ndk=1n/dk2μ(k)i=1n/kdij=1m/kdj\begin{aligned} & \sum_{i=1}^n\sum_{j=1}^m lcm(i,j)\\ =& \sum_{d=1}^n\sum_{k=1}^{\lfloor n/d\rfloor}\sum_{i=1}^{\lfloor n/kd\rfloor}\sum_{j=1}^{\lfloor m/kd\rfloor}\mu(k)\times d \times ik \times jk \\ =& \sum_{d=1}^nd\sum_{k=1}^{\lfloor n/d\rfloor}k^2\mu(k)\sum_{i=1}^{\lfloor n/kd\rfloor}i\sum_{j=1}^{\lfloor m/kd\rfloor}j\\ \end{aligned}
西k=1n/dk2μ(k)i=1n/kdij=1m/kdj\large 這個東西\sum\limits_{k=1}^{\lfloor n/d\rfloor}k^2\mu(k)\sum\limits_{i=1}^{\lfloor n/kd\rfloor}i\sum\limits_{j=1}^{\lfloor m/kd\rfloor}j\\ 看起來很整除分塊
g(n,m)=i=1nij=1mj,O(1)\large設g(n,m)=\sum\limits_{i=1}^{ n}i\sum\limits_{j=1}^{m}j,這個顯然可以O(1)算
k=1n/dk2μ(k)i=1n/kdij=1m/kd=k=1n/dk2μ(k)g(n/dk,m/dk)即\large \sum\limits_{k=1}^{\lfloor n/d\rfloor}k^2\mu(k)\sum\limits_{i=1}^{\lfloor n/kd\rfloor}i\sum\limits_{j=1}^{\lfloor m/kd\rfloor}\\=\sum\limits_{k=1}^{\lfloor n/d\rfloor}k^2\mu(k)g(\lfloor n/dk\rfloor,\lfloor m/dk\rfloor)顯然可以整除分塊

f(n,m)=k=1nk2μ(k)g(n/k,m/k)\large 再設f(n, m)=\sum\limits_{k=1}^{n}k^2\mu(k)g(\lfloor n/k\rfloor,\lfloor m/k\rfloor)
d=1ndk=1n/dk2μ(k)i=1n/kdij=1m/kdj    =d=1nd×f(n/d,m/d)\large 那麼\sum\limits_{d=1}^nd\sum\limits_{k=1}^{\lfloor n/d\rfloor}k^2\mu(k)\sum\limits_{i=1}^{\lfloor n/kd\rfloor}i\sum\limits_{j=1}^{\lfloor m/kd\rfloor}j\\ \ \ \ \ =\sum\limits_{d=1}^nd\times f(\lfloor n/d \rfloor, \lfloor m/d \rfloor)

O(n2)=O(n)就是兩個乘除分塊套在一起時間複雜度 O( \sqrt n^2)=O(n)
禁止套娃

code:

#include<bits/stdc++.h>
#define N 10000005
#define mod 20101009
#define int long long
using namespace std;
int prime[N], vis[N], mu[N], s[N], sz;
inline void init() {
	mu[1] = 1;
	for(int i = 2; i < N; i ++) {
		if(!vis[i]) {
			prime[++ sz] = i;
			mu[i] = mod - 1;
		}
		for(register int j = 1; j <= sz && prime[j] * i < N; j ++) {
			vis[prime[j] * i] = 1;
			if(i % prime[j] == 0) break;
			else mu[i * prime[j]] = mod - mu[i];
		}
	}	
	for(int i = 1; i < N; i ++) s[i] = (s[i - 1] + i * i % mod * mu[i]) % mod; 
}
int g(int x, int y) {
	return (x * (x + 1) / 2 % mod) * (y * (y + 1) / 2 % mod) % mod;
}
int f(int x, int y) {
	int ret = 0;
	for(int l = 1, r = 0; l <= x; l = r + 1) {
		r = min(x / (x / l), y / (y / l));
		ret += (s[r] - s[l - 1] + mod) * g(x / l, y / l) % mod, ret %= mod;
	}
	return ret;
}
int n, m;
signed main() {
	init();
	scanf("%lld%lld", &n, &m);
	if(n > m) swap(n, m);
	int ans = 0;
	for(int l = 1, r = 0; l <= n; l = r + 1) {
		r = min(n / (n / l), m / (m / l));
		ans += (r - l + 1) * (l + r) / 2 % mod * f(n / l, m / l) % mod, ans %= mod;
	}
	printf("%lld", ans);	
	return 0;
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章