文章目录
- 题目
-
- 题目大意:
- 数据范围
- 思路
- 代码
题目
vjudge传送门
Uva传送门
题目大意:
给出 1~n 的排列,可以通过一系列的交换成{1,2,3,…,n}
给定 n,k 求统计有多少个排列至少需要交换k次才能变成{1,2,3,…,n}
数据范围
1≤n≤21,0≤k<n1\le n\le 21,0\le k<n1≤n≤21,0≤k<n
思路
我们首先记 PPP 为排列 1,2,3,...,n{1,2,3,...,n}1,2,3,...,n
我们可以考虑假设现在有一个排列,要求至少交换多少次变成 PPP ?
首先知道每次交换必定为让一个数归位,人品好的时候会一下子归位两个
然后我们可以将位置有关联的分成一个个循环,各循环之间相互独立
然后又知道两个数为一循环需要交换1次,三个数为一循环需要交换2次,…,两个数为一循环需要交换1次,m个数为一循环需要交换m-1次
相当于这个排列有 xxx 个循环,那么就要交换 n?xn-xn?x 次
那么现在我们知道 交换 kkk 次,那么有 n?kn-kn?k 个循环
n个数分成m个圆排列求方案数是不是就是S1n个数分成m个圆排列求方案数是不是就是S1n个数分成m个圆排列求方案数是不是就是S1?
第一类斯特林数自己百度一下吧
代码
#include<set>
#include<map>
#include<stack>
#include<cmath>
#include<ctime>
#include<queue>
#include<cstdio>
#include<vector>
#include<climits>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
#define ULL unsigned long long
int read(){
int f=1,x=0;char c=getchar();while(c<'0'||'9'<c){
if(c=='-')f=-1;c=getchar();}while('0'<=c&&c<='9') x=(x<<3)+(x<<1)+c-'0',c=getchar();return f*x;
}
#define MAXN 21
ULL f[MAXN+5][MAXN+5];
int main(){
memset(f,0,sizeof(f));f[1][1]=1;for(int i=2;i<=MAXN;i++)for(int j=1;j<=i;j++)f[i][j]=f[i-1][j-1]+f[i-1][j]*(i-1);int n,k;while(~scanf("%d %d",&n,&k)&&(n+k))printf("%llu\n",f[n][n-k]);return 0;
}