隐藏
「bzoj4305」数列的GCD - 组合计数+容斥原理 | Bill Yang's Blog

路终会有尽头,但视野总能看到更远的地方。

0%

「bzoj4305」数列的GCD - 组合计数+容斥原理

题目大意

    给出一个长度为$N$的数列$a[i]$,$1\le a[i]\le M(1\le i\le N)$。
    现在问题是,对于$1$到$M$的每个整数$d$,有多少个不同的数列$b[1],b[2],\ldots,b[N]$,满足:
      $(1)1\le b[i]\le M(1\le i\le N)$;
      $(2)\gcd(b[1],b[2],\ldots,b[N])=d$;
      $(3)$恰好有$K$个位置$i$使得$a[i]=b[i] (1\le i\le N)$。
    输出答案对$1000000007$取模的值。


题目分析

因为有条件$2$,因此留下与修改成的数必须都是$d$的倍数。
先统计出$d$的倍数的数有$cnt$个。
我们需要从$cnt$个数中选出$n-k$个数保持其不变,方案数为$C_{cnt}^{n-k}$。
并使得其他的所有数变化并变化成$d$的倍数。
首先将其他的本来是$d$的倍数的数修改成其他$d$的倍数的数,共有$cnt-(n-k)$个数,有$\lfloor\frac md\rfloor-1$种修改方案,方案数为$(\lfloor\frac md\rfloor-1)^{cnt-(n-k)}$。
再将不是$d$的倍数的数修改为$d$的倍数,共有$n-cnt$个数,有$\lfloor\frac md\rfloor$种修改方案,方案数为$\lfloor\frac md\rfloor^{n-cnt}$。
将上述方案乘起来就是答案:

但不难发现,这会算多,因为这些数的最大公约数可能是$d$的倍数而不是$d$,因此“小容斥一波”得出解。


代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
#include<algorithm>
#include<iostream>
#include<iomanip>
#include<cstring>
#include<cstdlib>
#include<climits>
#include<vector>
#include<cstdio>
#include<cmath>
#include<queue>
using namespace std;

typedef long long LL;

inline const int Get_Int() {
int num=0,bj=1;
char x=getchar();
while(x<'0'||x>'9') {
if(x=='-')bj=-1;
x=getchar();
}
while(x>='0'&&x<='9') {
num=num*10+x-'0';
x=getchar();
}
return num*bj;
}

const int maxn=300005;
const LL mod=1e9+7;

LL n,m,k,Hash[maxn],fac[maxn],inv[maxn],invf[maxn],f[maxn];

LL C(int n,int m) {
return fac[n]*invf[n-m]%mod*invf[m]%mod;
}

LL Quick_Pow(LL a,LL b) {
LL sum=1;
for(; b; b>>=1,a=a*a%mod)if(b&1)sum=sum*a%mod;
return sum;
}

int main() {
n=Get_Int();
m=Get_Int();
k=Get_Int();
for(int i=1; i<=n; i++)Hash[Get_Int()]++;
fac[0]=invf[0]=inv[1]=1;
for(int i=1; i<=n; i++) {
fac[i]=fac[i-1]*i%mod;
if(i!=1)inv[i]=(mod-mod/i)*inv[mod%i]%mod;
invf[i]=invf[i-1]*inv[i]%mod;
}
for(int i=1; i<=m; i++) {
int cnt=0;
for(int j=1; (LL)i*j<=m; j++)cnt+=Hash[i*j];
if(cnt<n-k)continue; //无解
f[i]=C(cnt,n-k)*Quick_Pow(m/i-1,cnt-(n-k))%mod*Quick_Pow(m/i,n-cnt)%mod;
}
for(int i=m; i>=1; i--)
for(int j=2; (LL)i*j<=m; j++)f[i]=(f[i]-f[i*j]+mod)%mod;
for(int i=1; i<=m; i++)printf("%d ",f[i]);
return 0;
}
姥爷们赏瓶冰阔落吧~