FZU 2282 Wand,利用扩展欧几里得求逆元快速求C(n,m) , 错排公式推导

来源:互联网 发布:手机淘宝怎么降价提醒 编辑:程序博客网 时间:2024/06/05 21:07
传送门
Problem 2282 Wand

Accept: 30    Submit: 94
Time Limit: 1000 mSec    Memory Limit : 262144 KB

Problem Description

N wizards are attending a meeting. Everyone has his own magic wand. N magic wands was put in a line, numbered from 1 to n(Wand_i owned by wizard_i). After the meeting, n wizards will take a wand one by one in the order of 1 to n. A boring wizard decided to reorder the wands. He is wondering how many ways to reorder the wands so that at least k wizards can get his own wand.

For example, n=3. Initially, the wands are w1 w2 w3. After reordering, the wands become w2 w1 w3. So, wizard 1 will take w2, wizard 2 will take w1, wizard 3 will take w3, only wizard 3 get his own wand.

Input

First line contains an integer T (1 ≤ T ≤ 10), represents there are T test cases.

For each test case: Two number n and k.

1<=n <=10000.1<=k<=100. k<=n.

Output

For each test case, output the answer mod 1000000007(10^9 + 7).

Sample Input

2
1 1
3 1

Sample Output

1

题意:有n个数字,每个数字都有自己的位置,现在重新排序(任意排序),使得每个数字都不一定在原来的位置,求有多少种排序能使得至少有k个人在原来的位置。
思路:先保证有k个数字(n个数字选k个  C(n,k) )不变,剩下的n-k个数字,错排(我用d数组表示),d[i]表示i个数字错排种数。
d[1]=0,d[2]=1;
d[n]=(n-1)*(d[n-1]+d[n-2]);

LL   ans=1;//因为一定有一种排序(所有的数字位置都不变,也就是下面i=n的情况了)
for(  int   i=k;k<n;k++)
  ans+=d[n-i]*C(n,i);
上面的东西还能懂,有两个问题一个是错排公式,一个是C(n,i)一定要快速求,不然会超时。
错排:http://blog.csdn.net/liwen_7/article/details/7646451  
(博客内容如下:)
n各有序的元素应有n!种不同的排列。如若一个排列式的所有的元素都不在原来的位置上,则称这个排列为错排。任给一个n,求出1,2,……,n的错排个数Dn共有多少个。
递归关系式为:D(n)=(n-1)(D(n-1)+D(n-2))
D(1)=0,D(2)=1
可以得到:
错排公式为
 f(n) = n![1-1/1!+1/2!-1/3!+……+(-1)^n*1/n!] 
其中,n!=1*2*3*.....*n,
特别地,有0!=0,1!=1.


n 个不同元素的一个错排可由下述两个步骤完成: 
第一步,“错排” 1 号元素(将 1 号元素排在第 2 至第 n 个位置之一),有 n - 1 种方法。 
第二步,“错排”其余 n - 1 个元素,按如下顺序进行。视第一步的结果,若1号元素落在第 k 个位置,第二步就先把 k 号元素“错排”好, k 号元素的不同排法将导致两类不同的情况发生:
1、 k 号元素排在第1个位置,留下的 n - 2 个元素在与它们的编号集相等的位置集上“错排”,有 f(n -2) 种方法;
2、 k 号元素不排第 1 个位置,这时可将第 1 个位置“看成”第 k 个位置(也就是说本来准备放到k位置为元素,可以放到1位置中),于是形成(包括 k 号元素在内的) n - 1 个元素的“错排”,有 f(n - 1) 种方法。据加法原理,完成第二步共有 f(n - 2)+f(n - 1) 种方法。 
根据乘法原理, n 个不同元素的错排种数 
f(n) = (n-1)[f(n-2)+f(n-1)] (n>2) 。 

这个博客写的很清楚
我只解释一下我对2的理解:  
                 如果现在有1 2 3 4 5 这5个数字,有1 2 3 4 5 这五个位置,每个数字都不能去他自己的位置,并且其他位置都能去,这是错排,有d[5]种;    
                 若现在有    1 2 3 4 5这5个数字, 有1 2 3 4 6 这五个位置    并且我要求数字5不能到位置6,可以到位置1 2 3 4,这个是不是就跟上面的基本上一样了,那么这个也是d[5]种。
(我就解释这么多,其他内容你们应该可以看懂)

这个错排公式 f(n) = n![1-1/1!+1/2!-1/3!+……+(-1)^n*1/n!] 我也不怎么懂。

快速求 C(n,m)需要学习扩展欧几里得;
扩展欧几里得:
LL exgcd(LL a, LL b,LL &x,LL &y)//这里是引用,新的x,y,是由以前的x,y,的值得到{    if (b == 0)    { x = 1; y = 0; return a; }//x,y,的初始化    LL g = exgcd(b, a % b ,x ,y);    LL t = x - a / b * y;//回溯求逆元    x = y;    y = t;    return g;//g表示a和b的最大公约数}
扩展欧几里得能得到a*x+b*y=1;的一组解,那么a*x+b*y=c;的解又该怎么求,只需要将解扩大c/gcd(a,b)倍即可
现在解释为什么扩展欧几里得能快速求C(n,m),
C(n,m)=n!/ ( (n-m)!*m! )  因为数字都比较大所以都%mod ,这时候逆元就派上用场了,除以一个数就是乘以这个数的逆元,
用扩展欧几里得求出  (n-m)!*m! 对于 mod 的逆元,
(n-m)!*m! * x + mod * y=1;  x及为(n-m)!*m! 的逆元(mod>x>0)
LL fac[N];  //void init()  {      LL i;      fac[0]=1;      for (LL i = 1; i < N; i++)      fac[i] = fac[i - 1] * i % MOD;  }  LL exgcd(LL a, LL b, LL &x, LL &y) {      if (!b) {x = 1; y = 0; return a;}      LL d = exgcd(b, a % b, y, x);      y -= a / b * x;      return d;  }    LL inv(LL a, LL n) {      LL x, y;      exgcd(a, n, x, y);      return (x + n) % n;  }    LL C(LL n, LL m) {      return fac[n] * inv(fac[m] * fac[n - m] % MOD, MOD) % MOD;  }  
AC代码就不再贴了。

















原创粉丝点击