BZOJ4305 數列的GCD

也許更好的閱讀體驗

Description\mathcal{Description}


Solution\mathcal{Solution}

這裏就不用N,MN,M,還是n,mn,m寫的習慣些
直接計算一個方案是十分不方便的
所以考慮容斥
g(d)g\left(d\right)表示d  gcdd\ |\ gcd的方案數
aa中有cntcnt個數是dd的倍數
那麼有
g(d)=(md)ncnt(cntnk)(md1)k(ncnt)g\left( d\right) =\left( \dfrac {m}{d}\right) ^{n-cnt}\begin{pmatrix} cnt \\ n-k \end{pmatrix}\left( \dfrac {m}{d}-1\right) ^{k-\left(n-cnt\right)}

(md)ncnt\left( \dfrac {m}{d}\right) ^{n-cnt}表示有ncntn-cnt個數是必須修改的,每個有md\frac{m}{d}種數選擇

那麼還剩k(ncnt)k-\left(n-cnt\right)個數必須要修改,我們可將其寫爲cnt(nk)cnt-(n-k),這樣就是等價於要選nkn-k個數出來

(cntnk)(md1)k(ncnt)\begin{pmatrix} cnt \\ n-k \end{pmatrix}\left( \dfrac {m}{d}-1\right) ^{k-\left(n-cnt\right)}表示將這cntcnt個數修改k(ncnt)k-(n-cnt)個數,每個數因爲自己本身是dd的一個倍數,所以只有md\frac{m}{d}種選擇

f(d)f\left(d\right)表示gcd=dgcd=d的方案數

然後可以考慮莫比烏斯反演
顯然有
g(n)=n  df(d)\begin{aligned}g\left( n\right) =\sum _{n\ |\ d}f\left( d\right)\end{aligned}
則根據莫比烏斯反演,有
f(n)=n  dμ(dn)g(d)\begin{aligned}f\left( n\right) =\sum _{n\ |\ d}\mu \left( \dfrac {d}{n}\right) g\left( d\right)\end{aligned}

當然,莫比烏斯反演什麼的是不可能莫比烏斯反演的
直接容斥就可以啦
f(n)=g(n)ndf(d)\begin{aligned}f\left(n\right)=g\left(n\right)-\sum_{n|d}f(d)\end{aligned}
從大到小枚舉dd,直接計算即可

Code\mathcal{Code}

/*******************************
Author:Morning_Glory
LANG:C++
Created Time:2019年08月23日 星期五 08時14分25秒
*******************************/
#include <cstdio>
#include <fstream>
#define ll long long
using namespace std;
const int maxn = 300005;
const int mod = 1000000007;
//{{{cin
struct IO{
	template<typename T>
	IO & operator>>(T&res){
		res=0;
		bool flag=false;
		char ch;
		while((ch=getchar())>'9'||ch<'0')	 flag|=ch=='-';
		while(ch>='0'&&ch<='9') res=(res<<1)+(res<<3)+(ch^'0'),ch=getchar();
		if (flag)	 res=~res+1;
		return *this;
	}
}cin;
//}}}
int n,m,k;
int a[maxn],num[maxn];
ll fac[maxn],inv[maxn],f[maxn];
//{{{ksm
int ksm (int a,int b)
{
	a%=mod;
	int s=1;
	for (;b;b>>=1,a=1ll*a*a%mod)
		if (b&1)	s=1ll*s*a%mod;
	return s;
}
//}}}
inline ll C (int n,int m)
{
	return fac[n]*inv[m]%mod*inv[n-m]%mod;
}
int main()
{
	cin>>n>>m>>k;
	fac[0]=fac[1]=inv[0]=inv[1]=1;
	for (int i=1;i<=n;++i)	cin>>a[i],++num[a[i]];
	for (int i=2;i<=n;++i)	fac[i]=1ll*fac[i-1]*i%mod;
	for (int i=2;i<=n;++i)	inv[i]=(-mod/i*inv[mod%i]%mod+mod)%mod;
	for (int i=2;i<=n;++i)	inv[i]=1ll*inv[i]*inv[i-1]%mod;
	for (int i=m;i>=1;--i){
		int cnt=0;
		for (int j=i;j<=m;j+=i)	cnt+=num[j];
		if (cnt-n+k<0)	f[i]=0;
		else	f[i]=C(cnt,n-k)*ksm(m/i-1,cnt-n+k)%mod*ksm(m/i,n-cnt)%mod;
		for (int j=i<<1;j<=m;j+=i)	f[i]=1ll*(f[i]-f[j]+mod)%mod;
	}
	for (int i=1;i<=m;++i)	printf("%lld ",f[i]);
	return 0;
}

如有哪裏講得不是很明白或是有錯誤,歡迎指正
如您喜歡的話不妨點個贊收藏一下吧

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