【Codeforces】 CF1515E Phoenix and Computers

题目链接

点击打开链接

题目解法

考虑开电脑的过程相当于连通块的操作,可以不考虑每个点的真实位置,只考虑每个点在当前几个点中的相对位置
可以令 d p [ i ] [ j ] dp[i][j] dp[i][j] 表示有 i i i 个点,构成 j j j 个连通块的方案数,其中每个点之间有序
向这类维护连通块的 d p dp dp 有一个特殊的做法(可以包含 添加新的连通块,在连通块中添加新的元素,合并 2 2 2 个连通块 的操作)

  1. 添加新的连通块
    即新加一个元素构成一个连通块
    d p [ i ] [ j ] = d p [ i − 1 ] [ j − 1 ] ∗ j dp[i][j]=dp[i-1][j-1]*j dp[i][j]=dp[i1][j1]j,即考虑可以这个连通块在 j j j 个连通块的位置
  2. 添加新的元素
    有2种情况
    • 直接在前面或后面加一个元素
      d p [ i ] [ j ] = d p [ i − 1 ] [ j ] ∗ j ∗ 2 dp[i][j]=dp[i-1][j]*j*2 dp[i][j]=dp[i1][j]j2,即选一个连通块,考虑加在前面还是后面
    • 在前面或后面隔一位加一个元素,夹在中间的元素会自动加上
      d p [ i ] [ j ] = d p [ i − 1 ] [ j ] ∗ j ∗ 2 dp[i][j]=dp[i-1][j]*j*2 dp[i][j]=dp[i1][j]j2
  3. 合并2个连通块
    有2种情况
    • 2个连通块中间隔着2个元素,任选一个手动添加即可
      d p [ i ] [ j ] = d p [ i − 2 ] [ j + 1 ] ∗ j ∗ 2 dp[i][j]=dp[i-2][j+1]*j*2 dp[i][j]=dp[i2][j+1]j2
    • 2个连通块之间隔着3个元素,选中间一个手动添加即可
      d p [ i ] [ j ] = d p [ i − 3 ] [ j + 1 ] ∗ j dp[i][j]=dp[i-3][j+1]*j dp[i][j]=dp[i3][j+1]j

时间复杂度 O ( n 2 ) O(n^2) O(n2)

#include 
using namespace std;
typedef long long LL;
const int N(410);
int n,P,dp[N][N];
inline int read(){
	int FF=0,RR=1;
	char ch=getchar();
	for(;!isdigit(ch);ch=getchar()) if(ch=='-') RR=-1;
	for(;isdigit(ch);ch=getchar()) FF=(FF<<1)+(FF<<3)+ch-48;
	return FF*RR;
}
int main(){
	n=read(),P=read();
	dp[0][0]=1;
	for(int i=1;i<=n;i++)
		for(int j=1;j<=i;j++){
			//新增一个连通块
			(dp[i][j]+=(LL)dp[i-1][j-1]*j%P)%=P;
			//在块中新增元素
			(dp[i][j]+=(LL)dp[i-1][j]*j*2%P)%=P;
			if(i>=2) (dp[i][j]+=(LL)dp[i-2][j]*j*2%P)%=P;
			//合并块
			if(i>=2) (dp[i][j]+=(LL)dp[i-2][j+1]*j*2%P)%=P;
			if(i>=3) (dp[i][j]+=(LL)dp[i-3][j+1]*j%P)%=P;
		}
	printf("%d",dp[n][1]);
	return 0;
}
//dp[i][j]:有i个电脑,分成了j个连通块的方案数

你可能感兴趣的:(Codeforces,算法)