错位排列
对于n的一个排列(a_{1}, a_{2}, a_{3} ... a_{n})。如果所有的(a_{i}) 不等于 (i),那么这个排列称为错位排列
求解方法
考虑递推
前$i$个元素时如何进行状态转移?
(一)根据错位排列的定义,第$i$个元素肯定不会放在自己的位置上,故第$i$个元素的位置有$i-1$种选择。
(二)对于剩下的$i-1$个元素,选择其中任意一个元素$k$。此时$k$的位置有两种选择:
1. 放在第$i$个元素的位置上,宏观上相当于$i$与$k$的位置互换了。而剩下的i-2个元素依然要求错位排列。方案数为$f[i-2]$
2. 不放在第$i$个元素的位置上,则相当于剩下的$i-1$个元素全部进行错位排列。方案数为$f[i-1]$
综上,我们可以得到$$f[i] = (i-1) * ( f[i-1] + f[i-2] )$$
例题 选新娘
题目大意:一共有$N$对新婚夫妇,其中有$M$个新郎找错了新娘,求发生这种情况一共有多少种可能.
有$M$个新郎找错了,意味着有且只有$M$个人是错排,另外人都刚好恰合。于是先求出$f[M]$,表示长度为$M$的序列的错排。
然而这样就完了吗?并没有,还要看看是哪$M$个人。从$N$个人中选$M$个,也就是组合数(C_N^M),于是答案就是(C_N^M * f[M])
/*By QiXingzhi*/ #include <cstdio> #define r read() #define Max(a,b) (((a)>(b)) ? (a) : (b)) #define Min(a,b) (((a)<(b)) ? (a) : (b)) using namespace std; typedef long long ll; #define int ll const int N = 100010; const int M = 1010; const int INF = 1061109567; inline int read(){ int x = 0; int w = 1; register int c = getchar(); while(c ^ '-' && (c < '0' || c > '9')) c = getchar(); if(c == '-') w = -1, c = getchar(); while(c >= '0' && c <= '9') x = (x << 3) +(x << 1) + c - '0', c = getchar(); return x * w; } int n,m; int f[30]; inline int JieCheng(int x){ int res = 1; for(int i = 2; i <= x; ++i) res *= i; return res; } inline int C(int m, int n){ return JieCheng(n) / (JieCheng(m) * JieCheng(n-m)); } #undef int int main(){ #define int ll n = r; m = r; f[1] = 0; f[2] = 1; for(int i = 3; i <= m; ++i){ f[i] = (i - 1) * (f[i-2] + f[i-1]); } printf("%lld",f[m] * C(m,n)); return 0; }