bzoj4305 数列的GCD

传送门
由于要求所有的 d d d的答案,我们考虑容斥。
f ( d ) f(d) f(d)表示 g c d = d gcd=d gcd=d时的答案。用 F ( d ) F(d) F(d)表示 d ∣ g c d d|gcd dgcd的答案之和。那么我们有:
F ( n ) = ∑ n ∣ d f ( d ) ⇔ f ( n ) = ∑ n ∣ d μ ( d n ) F ( d ) F(n)=\sum_{n|d}f(d) \Leftrightarrow f(n)=\sum_{n|d} \mu(\frac{d}{n}) F(d) F(n)=ndf(d)f(n)=ndμ(nd)F(d)
上式为莫比乌斯反演的一种形式。
那么现在考虑求 F ( d ) 。 F(d)。 F(d) c n t d cnt_d cntd表示 a a a数组中有多少个 a i a_i ai d d d的倍数。
现在每个数都必须是 d d d的倍数。也就是说对于那些与 a a a不同的 b b b,有 m d \frac{m}{d} dm种选择。
现在我们要求有 k k k个不相同,也就是说有 N − k N-k Nk个是相同的。
那么这 N − k N-k Nk个相同的一定从 c n t d cnt_d cntd中选( d d d的倍数)。选不够贡献就是 0 0 0
相同的选完之后,选不相同的。那么有 c n t d − ( N − k ) cnt_d-(N-k) cntd(Nk)个只能选 ( m d − 1 ) (\frac{m}{d}-1) (dm1)个。因为不能和 a a a相同。剩下的就可以随便选了。于是我们得到 F F F的表达式:
F ( d ) = ( c n t d N − k ) ∗ ( m d − 1 ) c n t d − ( N − k ) ∗ ( m d ) N − c n t d F(d)=\tbinom{cnt_d}{N-k}*(\frac{m}{d}-1)^{cnt_d-(N-k)}*(\frac{m}{d})^{N-cnt_d} F(d)=(Nkcntd)(dm1)cntd(Nk)(dm)Ncntd
那么我们可以先 O ( m l o g ( m ) ) O(mlog(m)) O(mlog(m))处理出 c n t cnt cnt数组,然后在 O ( l o g ) O(log) O(log)的时间内求出 F F F(快速幂)。
然后对于每个 f ( i ) f(i) f(i),枚举 i i i的倍数。于是 m 1 + m 2 + m 3 + . . . \frac{m}{1}+\frac{m}{2}+\frac{m}{3}+... 1m+2m+3m+...,约为 O ( m l o g ( m ) ) O(mlog(m)) O(mlog(m))

写代码的时候又犯了一个sb错误。 + + v i s [ x ] ++vis[x] ++vis[x]写成了 v i s [ x ] = 1 vis[x]=1 vis[x]=1,无语。
看来还是变量名字取得不好啊,以后要取恰当一点。。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int mod=1e9+7,maxn=3e5+10;
int fac[maxn],ifac[maxn];
int n,m,k,cnt[maxn],vis[maxn],x,mx;
int P[maxn],mu[maxn],mark[maxn],tot=0;
inline int add(int x,int y){return x+y>=mod?x+y-mod:x+y;}
inline int dec(int x,int y){return x-y<0?x-y+mod:x-y;}
inline int mul(int x,int y){return (ll)x*y%mod;}
inline int quickpow(int a,int b,int ret=1){for(;b;b>>=1,a=mul(a,a))if(b&1)ret=mul(ret,a);return ret;}
inline int C(int n,int m){
	if(m>n) return 0;
	return mul(fac[n],mul(ifac[m],ifac[n-m]));
}
inline int F(int d){
	if(n-k>cnt[d]) return 0;
	return mul(C(cnt[d],n-k),mul(quickpow((m/d)-1,cnt[d]-n+k),quickpow(m/d,n-cnt[d])));
}
inline void linear_sieves(int N=maxn){
	mark[0]=mark[1]=mu[1]=1;
	for(int i=2;i<N;++i){
		if(!mark[i]) P[++tot]=i,mu[i]=-1;
		for(int j=1;j<=tot&&i*P[j]<N;++j){
			mark[i*P[j]]=1;
			if(i%P[j]) mu[i*P[j]]=-mu[i];
			else break;
		}
	}fac[0]=fac[1]=1;
	for(int i=2;i<N;++i) fac[i]=mul(fac[i-1],i);
	ifac[N-1]=quickpow(fac[N-1],mod-2);
	for(int i=N-2;i>=0;--i) ifac[i]=mul(ifac[i+1],i+1);
}
inline int read(){
	int x=0;char ch=getchar();
	while(!isdigit(ch)) ch=getchar();
	while(isdigit(ch)) x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
	return x;
}
int main(){
	linear_sieves(),n=read(),m=read(),k=read();
	for(int i=1;i<=n;++i) ++vis[x=read()],mx=max(mx,x);
	for(int i=1;i<=mx;++i)
		for(int j=i;j<=mx;j+=i)
			cnt[i]+=vis[j];
	for(int i=1;i<=m;++i){
		int ans=0;
		for(int j=i,t=1;j<=m;j+=i,++t){
			if(mu[t]==0) continue;
			if(mu[t]==1) ans=add(ans,F(j));
			if(mu[t]==-1)ans=dec(ans,F(j));
		}printf("%d ",ans);
	}
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值