bzoj 4314: 倍数?倍数! 数学

来源:互联网 发布:淘宝60多的halo烟油 编辑:程序博客网 时间:2024/04/27 21:43

       我们可以把集合改成序列,然后答案再/K!,这样就简单很多了。注意此时1,2,2算两次。

       实际上,考虑前K-1个数,我们随意取,这样最后一个数实际上是固定的;但是可能会出现重复的情况,那么此时我们统计最后两个数重复,也就是前K-2个数随意取,且满足前K-2个数的和S使得S+2x0(mod N)有解的方案。注意到该方程有解当且仅当S≡0(mod gcd(2,N)),那么对于特定的S方程的解的个数为gcd(2,N)……

       注意到这里面所有的统计都可以归结为f(k,n,p)表示取k个不同的0~N-1之间的数,其中有且仅有一个数出现p次的答案;那么我们k-1个随意取但需要满足前k-1个数的和S≡0(mod gcd(p,n)),也就是f(k-1,gcd(p,n),1)*方程解的个数;然后减去有一个数出现p+1次的答案f(k-1,n,p+1)*出现p+1次的数出现的位置(即k-1),然后注意边界即可。

AC代码如下:

#include<iostream>#include<cstdio>#include<cstring>#define ll long long#define mod 1000000007#define N 1005using namespace std;int n,m,f[N][N],ptt[N];int pw(int x,int y){int t=1; for (; y; y>>=1,x=(ll)x*x%mod) if (y&1) t=(ll)t*x%mod;return t;}int gcd(int x,int y){ return (y)?gcd(y,x%y):x; }int solve(int x,int y,int z){if (!x) return 1;if (y==1) return ptt[x];int d=gcd(y,z);if (z==1){int t=min(y,1001);if (f[x][t]==-1){f[x][t]=((ll)solve(x-1,d,1)*(m/y*d)-(ll)solve(x-1,y,2)*(x-1))%mod;if (f[x][t]<0) f[x][t]+=mod;}return f[x][t];}return (((ll)solve(x-1,d,1)*(m/y*d)-(ll)solve(x-1,y,z+1)*(x-1))%mod+mod)%mod;}int main(){scanf("%d%d",&m,&n);memset(f,-1,sizeof(f));int i,tmp=1;ptt[1]=m;for (i=2; i<=n; i++) ptt[i]=(ll)ptt[i-1]*(m-i+1)%mod;for (i=1; i<=n; i++) tmp=(ll)tmp*i%mod;printf("%d\n",(ll)solve(n,m,1)*pw(tmp,mod-2)%mod);return 0;}


by lych

2016.5.24

0 0