[BZOJ2693]jzptab

題目

傳送門 to darkBZOJ

思路

ans=i=1nj=1mijgcd(i,j)ans=\sum_{i=1}^{n}\sum_{j=1}^{m}\frac{i\cdot j}{\gcd(i,j)}

F(x,y)=i=1xj[1,y]gcd(i,j)=1ijF(x,y)=\sum_{i=1}^{x}\sum_{j\in[1,y]}^{\gcd(i,j)=1}i\cdot j

枚舉 gcd(i,j)\gcd(i,j) ,我們可以寫出

ans=d=1min(n,m)dF(nd,md)ddans=\sum_{d=1}^{\min(n,m)}\frac{d\cdot F\left(\left\lfloor\frac nd\right\rfloor,\left\lfloor\frac md\right\rfloor\right)\cdot d}{d}

再令

S(x,y)=i=1xj=1yijS(x,y)=\sum_{i=1}^{x}\sum_{j=1}^{y}i\cdot j

我們此時可以進行莫比烏斯反演(或者叫容斥),得到

F(x,y)=i=1min(x,y)i2μ(i)S(xi,yi)F(x,y)=\sum_{i=1}^{\min(x,y)}i^2\cdot\mu(i)\cdot S\left(\left\lfloor\frac xi\right\rfloor,\left\lfloor\frac yi\right\rfloor\right)

ans=d=1min(n,m)dF(nd,md)=d=1min(n,m)di=1+i2μ(i)S(nid,mid)\begin{aligned} \Rightarrow ans&=\sum_{d=1}^{\min(n,m)}d\cdot F\left(\left\lfloor\frac nd\right\rfloor,\left\lfloor\frac md\right\rfloor\right)\\ &=\sum_{d=1}^{\min(n,m)}d\sum_{i=1}^{+\infty}i^2\cdot\mu(i)\cdot S\left(\left\lfloor\frac n{id}\right\rfloor,\left\lfloor\frac m{id}\right\rfloor\right) \end{aligned}

枚舉 idid 的值,寫成

ans=D=1+S(nD,mD)iDi2μ(i)Dians=\sum_{D=1}^{+\infty}S\left(\left\lfloor\frac nD\right\rfloor,\left\lfloor\frac mD\right\rfloor\right)\sum_{i|D}i^2\cdot\mu(i)\cdot\frac{D}{i}

顯然前面可以整除分塊了,要做的就是求出 iDDμ(i)i\sum_{i|D}D\cdot\mu(i)\cdot i 的前綴和。

考慮使用 線性篩。這個函數是個積性函數,因爲,記 P(n)={xkx=n,kN+}P(n)=\{x|kx=n,k\in\N^+\} ,則 gcd(n,m)=1    {xyxP(n),yP(m)}=P(nm)\gcd(n,m)=1\;\Rightarrow\;\{xy|x\in P(n),y\in P(m)\}=P(nm)

至於 ipi\cdot p 的值怎麼計算呢,如果 pip|i 的話?考慮到 μ(i)\mu(i) 的存在使得質因數指數不超過 11 ,所以貢獻不爲零的約數 ii 沒有增加。唯一需要的是 DD 變成了原來的 pp 倍,相乘即可。

代碼

#include <cstdio>
#include <iostream>
#include <vector>
using namespace std;
typedef long long int_;
int readint(){
	int a = 0; char c = getchar(), f = 1;
	for(; c<'0'||c>'9'; c=getchar())
		if(c == '-') f = -f;
	for(; '0'<=c&&c<='9'; c=getchar())
		a = (a<<3)+(a<<1)+(c^48);
	return a*f;
}
template < typename T >
void getMin(T&a,const T&b){ if(b<a)a=b; }

const int MaxN = 10000005;
const int zxy = 100000009;

bool isPrime[MaxN];
int f[MaxN], mu[MaxN];
vector< int > primes;
void sievePrime(int n){
	f[1] = mu[1] = 1;
	for(int i=2; i<=n; ++i)
		isPrime[i] = true;
	for(int i=2,len=0; i<=n; ++i){
		if(isPrime[i]){
			primes.push_back(i);
			mu[i] = -1, ++ len;
			f[i] = (i-1ll*i*i)%zxy;
		}
		for(int j=0; j<len; ++j){
			if(primes[j] > n/i) break;
			isPrime[i*primes[j]] = 0;
			if(i%primes[j] == 0){
				mu[i*primes[j]] = 0;
				f[i*primes[j]] = 1ll*
				f[i]*primes[j]%zxy;
				break;
			}
			mu[i*primes[j]] = -mu[i];
			f[i*primes[j]] = 1ll*f[i]
			*f[primes[j]]%zxy;
		}
	}
}

int S(int a,int b){
	a = (1ll*a*(a+1)>>1)%zxy;
	b = (1ll*b*(b+1)>>1)%zxy;
	return 1ll*a*b%zxy;
}

int main(){
	sievePrime(MaxN-5);
	for(int i=2; i<=MaxN-5; ++i)
		f[i] = (f[i]+f[i-1]+zxy)%zxy;
	for(int T=readint(); T; --T){
		int n = readint();
		int m = readint();
		int ans = 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));
			int_ now = f[r]-f[l-1];
			now = (now%zxy+zxy)%zxy;
			now = now*S(n/l,m/l)%zxy;
			ans = (ans+now)%zxy;
		}
		printf("%lld\n",ans);
	}
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章