link: https://loj.ac/problem/6244
让我们设B=N-K,再设F(P)为从1~P+B中选P个数(有序的),且这P个数是一个错排(即不存在一个i使得 i=Ai ,其中A是选出的有序的P个数)的方案数。
再设G(P)为从1~P+B中选出P个数的方案数。
有几个显然的结论:
1.G(P)=P(P+B,P)
2.ANS=C(K,X)*F(K-X) ,考虑x个相同的是哪些元素。。。
3.G(P)=Σ F(i) * C(P,i) ,考虑有几个元素和原来相同。。。
因为3.可以用来二项式反演,然后再结合1.就可以求出F(K-X),然后直接得到答案。
#include<bits/stdc++.h> #define ll long long #define maxn 1000000 using namespace std; const int ha=1000000007; int jc[maxn+5],ni[maxn+5]; int N,X,K,B; inline int add(int x,int y){ x+=y; return x>=ha?x-ha:x; } inline int ksm(int x,int y){ int an=1; for(;y;y>>=1,x=x*(ll)x%ha) if(y&1) an=an*(ll)x%ha; return an; } inline void init(){ jc[0]=1; for(int i=1;i<=maxn;i++) jc[i]=jc[i-1]*(ll)i%ha; ni[maxn]=ksm(jc[maxn],ha-2); for(int i=maxn;i;i--) ni[i-1]=ni[i]*(ll)i%ha; } inline int P(int x,int y){ return x<y?0:jc[x]*(ll)ni[x-y]%ha; } inline int C(int x,int y){ return x<y?0:P(x,y)*(ll)ni[y]%ha; } inline int F(int x){ int an=0; for(int i=0;i<=x;i++) if((x-i)&1) an=add(an,ha-P(i+B,i)*(ll)C(x,i)%ha); else an=add(an,P(i+B,i)*(ll)C(x,i)%ha); return an; } inline int calc(){ return C(K,X)*(ll)F(K-X)%ha; } int main(){ init(); scanf("%d%d%d",&N,&K,&X); B=N-K; printf("%d\n",calc()); }