我们可以把集合改成序列,然后答案再/K!,这样就简单很多了。注意此时1,2,2算两次。
实际上,考虑前K-1个数,我们随意取,这样最后一个数实际上是固定的;但是可能会出现重复的情况,那么此时我们统计最后两个数重复,也就是前K-2个数随意取,且满足前K-2个数的和S使得S+2x≡0(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 1005
using 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