【BZOJ 4305】数列的GCD

传送门


problem

给出一个长度为 n n n 的数列 { a n } \{a_n\} {an} 1 ≤ a i ≤ m ( 1 ≤ i ≤ n ) 1\le a_i\le m(1\le i\le n) 1aim(1in)

现在问题是,对于 1 1 1 m m m 的每个整数 d d d,有多少个不同的数列 b 1 , b 2 , . . . , b n b_1, b_2, ..., b_n b1,b2,...,bn,满足:

  • 1 ≤ b i ≤ m ( 1 ≤ i ≤ n ) 1\le b_i\le m(1\le i\le n) 1bim(1in)
  • gcd ⁡ ( b 1 , b 2 , . . . , b n ) = d \gcd(b_1, b_2, ..., b_n)=d gcd(b1,b2,...,bn)=d
  • 恰好有 k k k 个位置 i i i 使得 a i ̸ = b i ( 1 ≤ i ≤ n ) a_i\not= b_i(1\le i\le n) ai̸=bi(1in)

注: gcd ⁡ ( x 1 , x 2 , . . . , x n ) \gcd(x_1,x_2,...,x_n) gcd(x1,x2,...,xn) x 1 , x 2 , . . . , x n x_1, x_2, ..., x_n x1,x2,...,xn 的最大公约数。

输出答案对 1 0 9 + 7 10^9+7 109+7 取模的值。

数据范围: 1 ≤ n , m ≤ 3 × 1 0 5 1\le n,m\le3\times10^5 1n,m3×105 1 ≤ k ≤ n 1\le k\le n 1kn 1 ≤ a i ≤ m 1\le a_i\le m 1aim


solution

f ( d ) f(d) f(d) 表示 gcd ⁡ ( b 1 , . . . , b n ) = d \gcd(b_1,...,b_n)=d gcd(b1,...,bn)=d 的答案, F ( d ) = ∑ d ∣ D f ( D ) F(d)=\sum_{d|D}f(D) F(d)=dDf(D)

那么我们可以先计算出 F ( d ) F(d) F(d),然后用莫比乌斯反演得到 f ( d ) f(d) f(d)

我们用 c n t d cnt_d cntd 表示在序列 { a n } \{a_n\} {an} 中,是 d d d 倍数的数的个数。先把 F ( d ) F(d) F(d) 的表达式放上来:

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)=\binom{cnt_d}{n-k}(\lfloor\frac m d\rfloor-1)^{cnt_d-(n-k)}(\lfloor\frac m d\rfloor)^{n-cnt_d} F(d)=(nkcntd)(dm1)cntd(nk)(dm)ncntd

接下来就是对这个式子的理解。

首先,由于序列 { b n } \{b_n\} {bn} gcd ⁡ = d \gcd=d gcd=d,所以每个数都必须是 d d d 的倍数。即如果不加任何限制,每个位置的选法有 ⌊ m d ⌋ \lfloor \frac m d\rfloor dm 种。

我们要求要有恰好 k k k 个位置使 a i ≠ b i a_i\ne b_i ai̸=bi,就要选恰好 ( n − k ) (n-k) (nk) 个位置使 a i = b i a_i=b_i ai=bi。由于只有 c n t d cnt_d cntd 个位置上可能有 a i = b i a_i=b_i ai=bi,所以我们可以从 c n t d cnt_d cntd 中选中 n − k n-k nk 个,钦定这些位置相等,那就是 ( c n t d n − k ) \binom{cnt_d}{n-k} (nkcntd)

剩下的 c n t d − ( n − k ) cnt_d-(n-k) cntd(nk) 个位置上,不能有位置相等,所以只有 ⌊ m d ⌋ − 1 \lfloor\frac m d\rfloor-1 dm1 种选法,就是 ( ⌊ m d ⌋ − 1 ) c n t d − ( n − k ) (\lfloor\frac m d\rfloor-1)^{cnt_d-(n-k)} (dm1)cntd(nk)

最后的 n − c n t d n-cnt_d ncntd 个位置, a i a_i ai b i b_i bi 已经没有机会相等了,这部分的贡献就是 ( ⌊ m d ⌋ ) n − c n t d (\lfloor\frac m d\rfloor)^{n-cnt_d} (dm)ncntd

那么最后反演一下,得到:

f ( x ) = ∑ x ∣ d μ ( d x ) F ( d ) f(x)=\sum_{x|d}\mu(\frac d x)F(d) f(x)=xdμ(xd)F(d)

然后枚举倍数计算答案即可。

时间复杂度 O ( n log ⁡ n ) O(n\log n) O(nlogn)


code

#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 300005
#define P 1000000007
using namespace std;
int n,m,k,Max;
int num[N],cnt[N],fac[N],ifac[N],F[N];
int sum,prime[N],mark[N],mu[N];
int add(int x,int y)  {return x+y>=P?x+y-P:x+y;}
int dec(int x,int y)  {return x-y< 0?x-y+P:x-y;}
int mul(int x,int y)  {return 1ll*x*y%P;}
int power(int a,int b,int ans=1){
	for(;b;b>>=1,a=mul(a,a))
		if(b&1)  ans=mul(ans,a);
	return ans;
}
void linear_sieves(){
	mu[1]=1;
	for(int i=2;i<N;++i){
		if(!mark[i])  prime[++sum]=i,mu[i]=-1;
		for(int j=1;j<=sum&&i*prime[j]<N;++j){
			mark[i*prime[j]]=1;
			if(i%prime[j]==0)  {mu[i*prime[j]]=0;break;}
			mu[i*prime[j]]=-mu[i];
		}
	}
}
void prework(){
	fac[0]=fac[1]=1;
	for(int i=2;i<N;++i)  fac[i]=mul(fac[i-1],i);
	ifac[N-1]=power(fac[N-1],P-2);
	for(int i=N-2;~i;--i)  ifac[i]=mul(ifac[i+1],i+1);
}
int C(int n,int m){
	if(n<m)  return 0;
	return mul(fac[n],mul(ifac[m],ifac[n-m]));
}
int calc(int d){
	if(n-k>cnt[d])  return 0;
	return mul(C(cnt[d],n-k),mul(power(m/d-1,cnt[d]-(n-k)),power(m/d,n-cnt[d])));
}
int main(){
	scanf("%d%d%d",&n,&m,&k);
	linear_sieves(),prework();
	for(int i=1,x;i<=n;++i){
		scanf("%d",&x);
		num[x]++,Max=max(Max,x);
	}
	for(int i=1;i<=Max;++i)
		for(int j=i;j<=Max;j+=i)
			cnt[i]+=num[j];
	for(int i=1;i<=Max;++i)  F[i]=calc(i);
	for(int i=1;i<=m;++i){
		int ans=0;
		for(int j=1;i*j<=m;++j){
			if(!mu[j])  continue;
			ans=(mu[j]==1)?add(ans,F[i*j]):dec(ans,F[i*j]);
		}
		printf("%d ",ans);
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值