FZU2282 Wand
题意
n个数字,要求至少k个数字位置不变,其余进行错排的方案数
分析
错排公式:
D(n)=(n-1)[D(n-2)+D(n-1)]
如果n个数字,i个数字位置不变,其余进行错排的的方案数是C(n,i)*D[n-i]
那么题目的答案显然就是从k枚举到n,然后把所有的方案数加起来,这样显然是正确的,但是等等!这样会超时(而且也会MLE)!因为k很 小而n比较大!
所以我们可以把问题反过来。枚举从0到k-1,把方案数加起来,再用n的全排列减去这个方案数的和,这样就可以在时间范围内解决啦
等等,WA掉了!一脸懵逼的开始试数据。当试了一个n=13,k=1的时候发现是个负的!也就是这个错排的方案数大于了全排列的方案数,所以相减变成了负的!可是这怎么可能!
这是可能的,因为,我们取模了!所以此时错排方案数还没有超过MOD值但是全排列超过了,所以反而全排列要小。为了解决这个我们只要在相减的时候先加上一个MOD再相减就可以了~
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
1 #include <cstdio> 2 #include <algorithm> 3 #include <cstring> 4 #include <iostream> 5 6 using namespace std; 7 const int maxn=10000+10; 8 const int MOD=1000000007; 9 int T,k; 10 long long n,ans; 11 long long c[maxn][105],D[maxn],A[maxn]; 12 void init(){ 13 D[0]=1; D[1]=0;D[2]=1; 14 for(int i=3;i<=10000;i++){ 15 D[i]=((i-1)*(D[i-1]+D[i-2])%MOD)%MOD; 16 } 17 c[1][0]=1,c[1][1]=1; 18 for(int i=2;i<=10000;i++){ 19 c[i][0]=1; 20 for(int j=1;j<=min(i,100);j++){ 21 c[i][j]=(c[i-1][j-1]+c[i-1][j])%MOD; 22 } 23 } 24 A[1]=1; 25 for(int i=2;i<=10000;i++){ 26 A[i]=(A[i-1]%MOD*i%MOD)%MOD; 27 } 28 return ; 29 } 30 int main(){ 31 init(); 32 scanf("%d",&T); 33 for(int t=1;t<=T;t++){ 34 ans=0; 35 scanf("%lld%d",&n,&k); 36 37 //cout<<D[n]<<endl; 38 //cout<<A[n]<<endl; 39 for(int i=0;i<k;i++){ 40 int res=(c[n][i]%MOD*D[n-i]%MOD)%MOD; 41 ans=(ans+res)%MOD; 42 } 43 ans=(A[n]-ans+MOD)%MOD; 44 /* for(int i=k;i<=n;i++){ 45 int res=(c[n][i]%MOD*D[n-i]%MOD)%MOD; 46 ans=(ans+res)%MOD; 47 }*/ 48 printf("%lld\n",ans); 49 } 50 return 0; 51 } 52 /* 53 100 54 5 1 55 76 56 7 4 57 92 58 8 5 59 141 60 13 1 61 -63772160 62 52 10 63 273085312 64 */