BZOJ4305: 数列的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。 
 
 
首先gcd=x的答案可以转化成gcd为x因数的答案,再容斥相减。
那么我们现在要求的就是,对于1到M的每个整数d,有多少个不同的数列b[1], b[2], ..., b[N],满足: 
(1)1<=b[i]<=M(1<=i<=N); 
(2)(b[1], b[2], ..., b[N])均是d的倍数; 
(3)恰好有K个位置i使得a[i]<>b[i](1<=i<=N) 
然后发现顺序与答案无关。
枚举d,计算有多少a[i]是d的倍数以及有多少[1,m]中的数是d的倍数x。那么要从a[i]中选出n-k个数保留,剩下a[i]中的有x-1中选择,最后的有x种选择。
时间复杂度O(nlogn)
#include<cstdio>
#include<cctype>
#include<queue>
#include<cmath>
#include<cstring>
#include<algorithm>
#define rep(i,s,t) for(int i=s;i<=t;i++)
#define dwn(i,s,t) for(int i=s;i>=t;i--)
#define ren for(int i=first[x];i!=-1;i=next[i])
using namespace std;
inline int read() {
    int x=0,f=1;char c=getchar();
    for(;!isdigit(c);c=getchar()) if(c=='-') f=-1;
    for(;isdigit(c);c=getchar()) x=x*10+c-'0';
    return x*f;
}
typedef long long ll;
const int maxn=300010;
const int mod=1000000007;
int n,m,k,S[maxn];
ll ans[maxn],xp[maxn],inv[maxn];
ll pow(int a,int n) {
    if(!n) return 1;
    ll ans=pow(a,n>>1);
    (ans*=ans)%=mod;if(n&1) (ans*=a)%=mod;
    return ans;
}
int main() {
    n=read();m=read();k=n-read();
    rep(i,1,n) S[read()]++;
    xp[0]=inv[0]=1;
    rep(i,1,n) xp[i]=(xp[i-1]*i)%mod,inv[i]=(inv[i-1]*pow(i,mod-2))%mod;
    rep(i,1,m) {
        int cnt=0,tot=0;
        for(int j=i;j<=m;j+=i) cnt+=S[j],tot++;
        if(cnt>=k) ans[i]=pow(tot-1,cnt-k)*pow(tot,n-cnt)%mod*xp[cnt]%mod*inv[k]%mod*inv[cnt-k]%mod;
    }
    dwn(i,m,1) for(int j=2*i;j<=m;j+=i) ans[i]=(ans[i]-ans[j]+mod)%mod;
    printf("%lld",ans[1]);rep(i,2,m) printf(" %lld",ans[i]);
    return 0;
}
View Code

 

posted @ 2015-11-18 18:59  wzj_is_a_juruo  阅读(220)  评论(0编辑  收藏  举报