定义一个长度为 \(n\) 的排列 \(A_1,A_2,...,A_n\) 是好的,当且仅当 \(A_{A_i}=i\) 对于所有的 \(i\).
给定一个长度为 \(k\) 的排列 \(B_1,B_2,...,B_k\) , 有多少不同的好的排列满足排列 \(B\) 作为子序列出现在该排列中?
\(1\leq k\leq n\leq 200\) , \(B\) 是一个 \(1,\cdots ,k\) 的排列.
由 \(i\) 向 \(A_i\) 连边,构成一张有向图.
那么, 满足 \(A_{A_i}=i\) 的序列 \(A\) 构成的图中只有长度为 \(2\) 和长度为 \(1\) 的环.
考虑 \(B\) 中每个数所在的环是怎样的.
- 长度为 \(1\) 的环,正好在它自己的位置上.
- 长度为 \(2\) 的环,
- 相连的点是 \(B\) 中一个数,也就是 \(1,\cdots ,k\) 中的一个数.
- 相连的点是 \(k+1,\cdots ,m\) 中的一个一个数.
观察可得,因为 \(B\) 中的相对顺序不能改变,所以如果是相连的点是 \(k+1\cdots m\) 中的一个数,对于合法的方案,这些点必须是 \(B_i\) 到 \(B_k\) . 那么,剩下的 \(B_1\) 到 \(B_{i-1}\) ,因为相对顺序强制固定,并且环的大小最多为 \(2\) ,那么,如果存在合法方案,那么此方案是唯一的 .
所以,考虑枚举 \(i\) ,先判断前 \(i\) 个数是是否存在一种合法方案 .
接着,剩下部分,\(B\) 中的后 \(k-i\) 个数要选择 \(n-k\) 中的 \(k-i\) 个位置构成环,但相对位置固定 .
方案数为 \(\dbinom{n-k}{k-i}\) .
剩下的 \(n-k-(k-i)\) 个位置可以*地构成大小为 \(1\) 或 \(2\) 的环 .
这个需要预处理 ,考虑 \(f(i)\) 表示剩下 \(i\) 个数有构成大小为 \(1\) 或 \(2\) 的环的方案数 .
转移方程如下, \(f(i)=f(i-1)+(i-1)f(i-2)\) .
考虑 \(i\) 单独构成大小为 \(1\) 的环,或者可以选择前 \(i-1\) 个数中的任意一个构成一个长度为 \(2\) 的环,剩下的数有 \(f(i-2)\) 种构环方式 .
那么此时对于当前 \(i\) 的方案数就是 \(\dbinom{n-k}{k-i}f(n-k-(k-i))\) 种方法 .
但是,注意到此题没有模数,说明需要高精度 ,只需要加法和乘法就可以了 .
时间复杂度 : \(O(n^3)\)
空间复杂度 : \(O(n^3)\)
code
#include<bits/stdc++.h>
using namespace std;
inline int read(){
char ch=getchar();
while(ch<'0'||ch>'9')ch=getchar();
int res=0;
while(ch>='0'&&ch<='9'){
res=res*10+ch-'0';
ch=getchar();
}
return res;
}
vector<int>operator+(vector<int>a,vector<int>b){
vector<int>c;
int tmp=0,i=0;
for(int i=0;i<max((int)a.size(),(int)b.size());i++){
if(i<(int)a.size())tmp+=a[i];
if(i<(int)b.size())tmp+=b[i];
c.push_back(tmp%10);
tmp/=10;
}
while(tmp>0){
c.push_back(tmp%10);
tmp/=10;
}
return c;
}
vector<int>operator*(vector<int>a,vector<int>b){
vector<int>c((int)a.size()+(int)b.size()-1,0);
for(int i=0;i<(int)a.size();i++)for(int j=0;j<(int)b.size();j++)
c[i+j]+=a[i]*b[j];
int tmp=0;
for(int i=0;i<(int)c.size();i++){
tmp+=c[i];
c[i]=tmp%10;
tmp/=10;
}
while(tmp>0){
c.push_back(tmp%10);
tmp/=10;
}
return c;
}
int n,m;
int b[210],tmp[210];
vector<int>comb[210][210];
vector<int>dp[210];
bool vis[210];
int rk[210];
int main(){
freopen("permutation.in","r",stdin);
freopen("permutation.out","w",stdout);
ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
comb[0][0].push_back(1);
for(int i=1;i<210;i++){
comb[i][0].push_back(1);
for(int j=1;j<=i;j++){
comb[i][j]=comb[i-1][j-1]+comb[i-1][j];
}
}
dp[0].push_back(1);
for(int i=1;i<210;i++){
vector<int>val;
val.push_back(i-1);
dp[i]=dp[i-1];
if(i>=2)dp[i]=dp[i]+val*dp[i-2];
}
n=read();m=read();
for(int i=1;i<=m;i++)b[i]=read();
vector<int>ans;
ans.push_back(0);
for(int i=0;i<=m;i++)if(n-m>=m-i){
for(int j=1;j<=i;j++)tmp[j]=b[j];
sort(tmp+1,tmp+i+1);
for(int j=1;j<=i;j++)rk[tmp[j]]=j;
bool ok=true;
for(int j=1;j<=i;j++){
int pos=rk[b[j]];
if(rk[b[pos]]!=j)ok=false;
}
if(ok){
ans=ans+comb[n-m][m-i]*dp[n-m-m+i];
}
}
for(int i=(int)ans.size()-1;i>=0;i--)
putchar('0'+ans[i]);
putchar('\n');
return 0;
}