nflsoj 20034 #12433. 「NOIP2021模拟赛0916南外」排列

定义一个长度为 \(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. 长度为 \(1\) 的环,正好在它自己的位置上.
  2. 长度为 \(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;
} 
上一篇:nflsoj 20034 #12421. 「NOIP2021模拟赛0911北大附」冒泡排序


下一篇:nflsoj 20034 #12423. 「NOIP2021模拟赛0913华二」二进制式