国王的秘书 容斥原理 排列组合

国王的秘书

题目描述

国王需要向他所有的传道人分发指示。由于每位部长都需要一套独特的指示,他会为每位部长写一封个性化的信件,并将其交给他的秘书递送。

不幸的是,国王的秘书是一个非常粗心的家伙。他忘记了每位部长都有自己的个性化信件,而是随机向每位部长发信。在他意识到自己的错误后,他开始逐一召唤传道人,并询问他们是否收到了正确的信件。到目前为止,他已经召集了N名部长中的K位,并且令他感到恐惧的是,K位部长都没有收到正确的信件。

国王对他的秘书感到愤怒,但他决定给他最后一次机会来挽救他的工作。他问秘书以下问题。他有多少种方式分发这些信件,以便能够出现现状?换句话说,这些信件的分配方式有多少,以至于到目前为止所调用的每个K部长都写了一封错误的信?如果至少有一位部长获得不同的信件,则认为两种方式不同。如果秘书能够正确回答这个问题,他可以继续工作。你的工作是通过为他计算正确答案来帮助秘书。由于答案可能非常大,因此返回模数1,000,000,007。

输入格式

多组测试数据。

第一行,一个整数G,表示有G组测试数据。 1 <= G <= 10

每组测试数据格式:

   一行,两个整数,N和K。1 <= N <= 1000, 1<=K<=12。1 <= k <=N。

输出格式

共G行,每行一个整数。

输入样例

10
2 1
3 1
9 9
5 1
6 2
3 3
7 4
9 1
4 2
3 2

输出样例

1
4
133496
96
504
2
2790
322560
14
3

解题思路

本题大意:K位部长都没有收到正确的信的情况有多少种?

根据题意,我们只需计算前K个部长没有收到正确信件的情况就行了,其他部长可以随意排列。

但是直接从正面思考,这个想法是错误的,而且我们发现这样算特别难算。

从反面思考问题,答案=所有情况-至少有一个部长拿对信封的情况。

我们可以在容斥原理这个角度入手,那么我们可以想到:在这K个人中至少1人拿对信件的情况是 ( n − 1 ) ! × C K 1 (n-1)!\times C_K^1 (n1)!×CK1种;
但是在这些情况中会出现2个人拿对信件的情况,所以要减去 ( n − 2 ) ! × C K 2 (n-2)!\times C_K^2 (n2)!×CK2
紧接着我们又发现减去的时候,减多了一些情况,这些情况是3个人拿对信件的情况,所以加上 ( n − 3 ) ! × C K 3 (n-3)!\times C_K^3 (n3)!×CK3
⋯   ⋯ \cdots\space\cdots  

根据上面的思路,我们可以直接推出本题的答案: ( n − 1 ) ! × C K 1 − ( n − 2 ) ! × C K 2 + ( n − 3 ) ! × C K 3 ⋯ (n-1)!\times C_K^1-(n-2)!\times C_K^2+(n-3)!\times C_K^3\cdots (n1)!×CK1(n2)!×CK2+(n3)!×CK3 i i i是奇数时加上结果,当 i i i是偶数时减去结果


代码

/*
         m
C(n,m)=C
         n
		 
从正面想得到一个错误的思路:n!-(n-1)!*k+(n-2)!*(k-1)...
反过来想,答案=所有情况-K个人中至少有1个拿到对的信件的情况
  K个人中至少有1个拿到对的信件的情况
=(n-1)!*C(k,1)-(n-2)!*C(k,2)+(n-3)!*C(k,3)...
C(k,i):在K个人中有任意i个人拿对
(n-i)!:剩下的n-i个人拿到信件的所有情况 	  
*/

#include<iostream>
#include<fstream>
#include<algorithm>

using namespace std;
long long G,C[30][30];
long long a[1005];
long long ans;
int N,K;

int main()
{
    
    
	 a[0]=1;a[1]=1;
	 for(int i=2;i<=1002;i++)
		 a[i]=a[i-1]*i%1000000007;//求阶乘
	 for(int i=0;i<=25;i++)
	 {
    
    
		 C[i][i]=1;
		 C[i][0]=1;
	 }
	 for(int i=1;i<=25;i++)
		for(int j=1;j<i;j++)
			C[i][j]=C[i-1][j]+C[i-1][j-1];//求组合数
	 freopen("2786.in","r",stdin);
	 freopen("2786.out","w",stdout);
     cin>>G;
	 for(int gr=1;gr<=G;gr++)
	 {
    
    
		 cin>>N>>K;
		 ans=a[N];
		 for(int i=1;i<=K;i++)
		 {
    
    
			 if(i%2==0)
			 {
    
    
				 ans+=C[K][i]*a[N-i]%1000000007;
				 ans%=1000000007;
			 }
			 else
			 {
    
    
				 ans-=C[K][i]*a[N-i]%1000000007;
				 if(ans<0) ans+=1000000007;
				 else
					ans%=1000000007;
			 }
				 
		 }
		 cout<<ans<<endl;
	 }

    return 0;
}

猜你喜欢

转载自blog.csdn.net/bell041030/article/details/89276999