比赛时没想到方法,其实也是个常规题,中下难度。还是自己太弱了
题意:
给定n,m,k。A1、A2、...、An,对于每个1<=d<=m,求b1、b2、..、bn,满足,1<=bi<=m,切恰好有k个位置ai != bi,且他们的最大公约数为d。
思路:
Fd 为满足上述条件的最大公约数为d的方案数。
Gd为满足上述条件的公约数为d的方案数。
则Gd = sum{Fa,d|a(d能整除a)}
Gd 则为原问题的一个组合方案数。略。
已知Gd,倒着求Fd。复杂度O{m/1 +m/2 + m/3 +....+m/m} = O(m lg m)
![](https://i-blog.csdnimg.cn/blog_migrate/8f900a89c6347c561fdf2122f13be562.gif)
![](https://i-blog.csdnimg.cn/blog_migrate/961ddebeb323a10fe0623af514929fc1.gif)
//那么简单都想不到,真是弱爆了啊!!!! //那么简单都想不到,真是弱爆了啊!!!! #include<stdio.h> #include<math.h> #include<string.h> #define LL long long #define MOD 1000000007 #define N 300010 int n, m,q; LL power(LL a,LL b,LL c){ LL res = 1; while (b){ if (b&1){ res = (res * a) % c; } a = (a*a) % c; b /= 2; } return res; } LL fac[N],vfac[N]; void init(){ fac[0] = 1; vfac[0] = 1; for (int i=1;i<N;i++){ fac[i] = (fac[i-1] * i) % MOD; vfac[i] = power(fac[i],MOD-2,MOD); } return ; } LL comp(LL a,LL b){ return ((fac[a]*vfac[a - b]% MOD) *vfac[b]) % MOD; } int cnt[N]; LL ans[N]; int main(){ init(); //printf("%lld\n",comp(5,5)); while (scanf("%d%d%d",&n,&m,&q)==3){ memset(cnt,0,sizeof(cnt)); for (int i=1;i<=n;i++){ int x; scanf("%d",&x); cnt[x]++; } for (int d = m;d>=1;d--){ int x = 0; LL sum = 0; for (int i = d;i<=m;i+=d){ x += cnt[i]; if (i > d){ sum = (sum + ans[i]) % MOD; } } x = n - x; if (x > q){ ans[d] = 0; continue; } LL tmp1 = power(m/d,x,MOD); LL tmp2 = power(m/d -1,q-x,MOD); LL tmp3 = comp(n-x,q-x); ans[d] = (((((tmp1 * tmp2)%MOD)*tmp3)%MOD - sum) % MOD + MOD)%MOD; } for (int i=1;i<m;i++) printf("%I64d ",ans[i]); printf("%I64d\n",ans[m]); } }