bzoj4305: 数列的GCD

4305: 数列的GCD

Description

给出一个长度为N的数列{a[n]},1<=a[i]<=M(1<=i<=N)。 
现在问题是,对于1到M的每个整数d,有多少个不同的数列b[1], b[2], ..., b[N],满足: 
(1)1<=b[i]<=M(1<=i<=N); 
(2)gcd(b[1], b[2], ..., b[N])=d; 
(3)恰好有K个位置i使得a[i]<>b[i](1<=i<=N) 
注:gcd(x1,x2,...,xn)为x1, x2, ..., xn的最大公约数。 
输出答案对1,000,000,007取模的值。 

Input

第一行包含3个整数,N,M,K。 
第二行包含N个整数:a[1], a[2], ..., a[N]。 

Output

输出M个整数到一行,第i个整数为当d=i时满足条件的不同数列{b[n]}的数目mod 1,000,000,007的值。 

Sample Input

3 3 3
3 3 3

Sample Output

7 1 0

HINT

当d=1,{b[n]}可以为:(1, 1, 1), (1, 1, 2), (1, 2, 1), (1, 2, 2), (2, 1, 1), (2, 1, 2), (2, 2, 1)。 
当d=2,{b[n]}可以为:(2, 2, 2)。 
当d=3,因为{b[n]}必须要有k个数与{a[n]}不同,所以{b[n]}不能为(3, 3, 3),满足条件的一个都没有。 
对于100%的数据,1<=N,M<=300000, 1<=K<=N, 1<=a[i]<=M。 
 
题解:
 
思想很不错的一道题目,有k个不相同的位置,那么也就是只有n-k个位置相同,考虑A中如果能有一些数使得它们的GCD为d,那么它们肯定是d的倍数。
 
我们可以枚举d,用筛法算出d倍数的个数sum,相同的方案数为C(sum,n-k),我们还要把sum-(n-k)个强制变为不同
 
即个数为(m/d-1)sum-n+k,另外的n-sum个数方案为(m/d)n-sum,再考虑容斥就行了。
#include<stdio.h>
#include<iostream>
using namespace std;
const int N=300005;
const int M=1000000007;
#define ll long long 
int n,m,k,i,j,a[N],p[N];
ll fac[N],inv[N],f[N];
ll C(int n,int m)
{
	return fac[n]*inv[m]%M*inv[n-m]%M;
}
ll powmod(ll a,int b)
{
	ll ans=1;
	while(b)
	{
		if(b&1) ans=ans*a%M;
		a=a*a%M;
		b>>=1;
	}
	return ans;
}
int main()
{
	scanf("%d%d%d",&n,&m,&k);
	for(i=1;i<=n;i++) 
	{
		scanf("%d",&a[i]);
		p[a[i]]++;
	}
	inv[0]=inv[1]=1;
	for(i=2;i<=n;i++) inv[i]=inv[M%i]*(M-M/i)%M;
	for(i=2;i<=n;i++) inv[i]=inv[i]*inv[i-1]%M;
	fac[0]=1;
	for(i=1;i<=n;i++) fac[i]=fac[i-1]*i%M;
	for(i=m;i>=1;i--)
	{
		int sum=0;
		for(j=i;j<=m;j+=i) sum+=p[j];
		if(sum-n+k<0) f[i]=0;else
		f[i]=C(sum,n-k)*powmod(m/i-1,sum-n+k)%M*powmod(m/i,n-sum)%M;
		for(j=i<<1;j<=m;j+=i) f[i]=(f[i]-f[j]+M)%M;
	}
	for(i=1;i<=m;i++)
	{
		printf("%lld",f[i]);
		if(i<m) printf(" ");
	}
	return 0;
}

  

 
posted @ 2016-07-31 09:16  lwq12138  阅读(334)  评论(0编辑  收藏  举报