bzoj 3601: 一個人的數論(莫比烏斯反演 + 高斯消元 + 積性函數)

在這裏插入圖片描述


題目要求的是 f(n,d)=i=1n[gcd(i,n)=1]idf(n,d) = \displaystyle\sum_{i = 1}^n[gcd(i,n) = 1]i^d

h(x)=i=1n[gcd(i,n)=x]idh(x) = \displaystyle\sum_{i = 1}^n[gcd(i,n) = x]i^dH(x)=xph(p)H(x) = \displaystyle\sum_{x|p}h(p)

(從貢獻角度)等價於 H(x)=i=1n[xgcd(i,n)]idH(x) = \displaystyle\sum_{i = 1}^n[x|gcd(i,n)]i^d
根據莫比烏斯反演,h(x)=xpH(p)μ(px)h(x) = \displaystyle\sum_{x | p}H(p)*\mu(\frac{p}{x}),得到
h(1)=p=1nμ(p)i=1n[pgcd(i,n)]idh(1) = \sum_{p = 1}^n\mu(p)*\displaystyle\sum_{i = 1}^n[p|gcd(i,n)]i^dh(1)=pnμ(p)i=1np(ip)d=pnμ(p)pdi=1npidh(1) = \sum_{p | n}\mu(p)*\displaystyle\sum_{i = 1}^{\frac{n}{p}}(ip)^d=\sum_{p | n}\mu(p)*p^d\displaystyle\sum_{i = 1}^{\frac{n}{p}}i^d
到這一步似乎無法做下去了,因爲 n 特別大。此時只能根據題解大膽猜測,數據這麼大還給的是質因子分解的形式,可能要用積性函數來做,因爲積性函數滿足 f(xy)=f(x)f(y)[gcd(x,y)=1]f(x * y) = f(x) * f(y) * [gcd(x,y) = 1],可以計算每一項質因子的貢獻最後乘起來得到答案。

觀察發現這個式子是一個接近卷積的形式,前兩個函數都是積性函數,根據經驗可以得知後面一個求和式是一個以 np\frac{n}{p} 爲自變量的 d+1d + 1 次多項式,但這個多項式並不是一個積性函數。

注意到要將整個式子湊成一個積性函數,右邊那個和式必須是一個以 np\frac{n}{p} 爲變量的積性函數,這樣整個式子可以湊成一個 Dirichlet 卷積,進一步根據題解大膽嘗試,將右邊d+1d + 1 以多項式的形式傳入參數 np\frac{n}{p} 代入得到:h(1)=pnμ(p)pdi=0d+1bi(np)ih(1) =\sum_{p |n}\mu(p)*p^d\displaystyle\sum_{i = 0}^{d + 1}b_i*({\frac{n}{p}})^i改變枚舉項,先枚舉 ii,得到h(1)=i=0d+1bipnμ(p)pd(np)ih(1) =\sum_{i = 0}^{d + 1}b_i\sum_{p |n}\mu(p)*p^d\displaystyle({\frac{n}{p}})^i

這個時候右邊那部分就是積性函數了,並且由於 μ(x)\mu(x) 函數的特性,每一項 piaip_i^{a_i} 只有 1pi 有貢獻,只要計算出這個多項式的所有係數 bi 就可以在規定時間內求解。

對於多項式求係數 bi 有兩種方法:拉格朗日插值多點插值,高斯消元

拉格朗日多點插值求解多項式係數還不會,且這裏 d100d \leq 100,採用高斯消元進行求解


代碼:

#include<bits/stdc++.h>
using namespace std;
const int mod = 1e9 + 7;
typedef long long ll;
const int maxn = 1e3 + 10;
int k,w;
int p[maxn],a[maxn],b[maxn];
int x[maxn][maxn],y[maxn];
inline int read() {
	int x=0,f=1;char ch;
	while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
	while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
	return x*f;
}

inline int add(int x, int y) {
  	x += y;
  	if (x >= mod) {
    	x -= mod;
  	}
  	return x;
}

inline int sub(int x, int y) {
  	x -= y;
  	if (x < 0) {
    	x += mod;
  	}
  	return x;
}

inline int mul(int x, int y) {
  	return (long long) x * y % mod;
}
int fpow(int a,int b) {
	int r = 1;
	while (b) {
		if (b & 1) r = mul(r,a);
		a = mul(a,a);
		b >>= 1;
	}
	return r;
}
void Gauss(int x[maxn][maxn],int y[maxn],int n) {
	for (int i = 0; i <= n; i++) {
		for (int j = 0; j < i; j++) {
			int tp = mul(fpow(x[i][i],mod - 2),x[j][i]);
			for (int k = 0; k <= n; k++) {
				x[j][k] = sub(x[j][k],mul(tp,x[i][k]));
			}
			y[j] = sub(y[j],mul(tp,y[i]));
		}
		for (int j = i + 1; j <= n; j++) {
			int tp = mul(fpow(x[i][i],mod - 2),x[j][i]);
			for (int k = 0; k <= n; k++) {
				x[j][k] = sub(x[j][k],mul(tp,x[i][k]));
			}
			y[j] = sub(y[j],mul(tp,y[i]));
		}
	}
}
int main() {
	k = read(); w = read();
	for (int i = 1; i <= w; i++)
		p[i] = read(), a[i] = read();
	y[0] = 0;
	for (int i = 0; i <= k + 1; i++) {
		for (int j = 0; j <= k + 1; j++)
			x[i][j] = fpow(i,j);
		if (i > 0) y[i] = add(y[i - 1],fpow(i,k));
	}
	Gauss(x,y,k + 1);
	for (int i = 0; i <= k + 1; i++)
		b[i] = mul(y[i],fpow(x[i][i],mod - 2));
	int sum = 0, ans = 1;
	for (int i = 0; i <= k + 1; i++) {
		ans = 1; 
		for (int j = 1; j <= w; j++) {
			int res = 0;
			int t1 = fpow(p[j],a[j]), t2 = fpow(p[j],a[j] - 1);
			res = add(res,fpow(t1,i));
			res = sub(res,mul(fpow(t2,i),fpow(p[j],k)));
			ans = mul(ans,res);
		}	
		sum = add(sum,mul(b[i],ans));
	}
	printf("%d\n",sum);
	return 0;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章