BZOJ4305 数列的GCD

也许更好的阅读体验

D e s c r i p t i o n \mathcal{Description} Description


S o l u t i o n \mathcal{Solution} Solution

这里就不用 N , M N,M N,M,还是 n , m n,m n,m写的习惯些
直接计算一个方案是十分不方便的
所以考虑容斥
g ( d ) g\left(d\right) g(d)表示 d   ∣   g c d d\ |\ gcd d  gcd的方案数
a a a中有 c n t cnt cnt个数是 d d d的倍数
那么有
g ( d ) = ( m d ) n − c n t ( c n t n − k ) ( m d − 1 ) k − ( n − c n t ) 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)} g(d)=(dm)ncnt(cntnk)(dm1)k(ncnt)

( m d ) n − c n t \left( \dfrac {m}{d}\right) ^{n-cnt} (dm)ncnt表示有 n − c n t n-cnt ncnt个数是必须修改的,每个有 m d \frac{m}{d} dm种数选择

那么还剩 k − ( n − c n t ) k-\left(n-cnt\right) k(ncnt)个数必须要修改,我们可将其写为 c n t − ( n − k ) cnt-(n-k) cnt(nk),这样就是等价于要选 n − k n-k nk个数出来

( c n t n − k ) ( m d − 1 ) k − ( n − c n t ) \begin{pmatrix} cnt \\ n-k \end{pmatrix}\left( \dfrac {m}{d}-1\right) ^{k-\left(n-cnt\right)} (cntnk)(dm1)k(ncnt)表示将这 c n t cnt cnt个数修改 k − ( n − c n t ) k-(n-cnt) k(ncnt)个数,每个数因为自己本身是 d d d的一个倍数,所以只有 m d \frac{m}{d} dm种选择

f ( d ) f\left(d\right) f(d)表示 g c d = d gcd=d gcd=d的方案数

然后可以考虑莫比乌斯反演
显然有
g ( n ) = ∑ n   ∣   d f ( d ) \begin{aligned}g\left( n\right) =\sum _{n\ |\ d}f\left( d\right)\end{aligned} g(n)=n  df(d)
则根据莫比乌斯反演,有
f ( n ) = ∑ n   ∣   d μ ( d n ) 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)=n  dμ(nd)g(d)

当然,莫比乌斯反演什么的是不可能莫比乌斯反演的
直接容斥就可以啦
f ( n ) = g ( n ) − ∑ n ∣ d f ( d ) \begin{aligned}f\left(n\right)=g\left(n\right)-\sum_{n|d}f(d)\end{aligned} f(n)=g(n)ndf(d)
从大到小枚举 d d d,直接计算即可

C o d e \mathcal{Code} 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;
}

如有哪里讲得不是很明白或是有错误,欢迎指正
如您喜欢的话不妨点个赞收藏一下吧

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值