[CF1515E]Phoenix and Computers

壹、题目描述 ¶

传送门 to CF

贰、题解 ¶

题解注意到了我们最后开电脑地序列构成是 "一段手动开 + 一个自动开 + 一段手动开 + 一个自动开 + ... + 一个自动开 + 一段手动开",如果你手动开一个长度为 \(k\) 的电脑序列,方案数为 \(2^{k-1}\),然后,题解就设置状态 \(f_{i,j}\) 表示前 \(i\) 个电脑,前 \(i-1\) 个手动开,第 \(i\) 个自动开的方案数,然后进行转移。

更详细的解说看这里

更想写的是连续段 \(\tt DP\),或者说插头 \(\tt DP\)?定义状态 \(f_{i,j,k}\) 表示有 \(i\) 个灯已经亮了(不管手动还是自动),有 \(j\) 个亮着的灯的段,有 \(k\) 个段靠墙了,那么最后的答案就是 \(f_{n,1,2}\),对于转移,我们讨论一下它可能放的位置即可。注意每种情况点亮的灯的个数。

值得一提的是题解复杂度为 \(\mathcal O(n^3)\),然而连续段 \(\tt DP\) 的复杂度仅仅 \(\mathcal O(n^2)\)

叁、参考代码 ¶

这是插头 \(\tt DP\) 的代码。

#include<cstdio>
#include<queue>
#include<cmath>
#include<cstring>
#include<algorithm>
using namespace std;

#define Endl putchar('\n')
#define mp(a, b) make_pair(a, b)
#define rep(i, l, r) for(int i=(l), i##_end_=(r); i<=i##_end_; ++i)
#define fep(i, l, r) for(int i=(l), i##_end_=(r); i>=i##_end_; --i)
#define fi first
#define se second
typedef long long ll;
template<class T>inline T fab(T x){ return x<0? -x: x; }
template<class T>inline T readin(T x){
	x=0; int f=0; char c;
	while((c=getchar())<'0' || '9'<c) if(c=='-') f=1;
	for(x=(c^48); '0'<=(c=getchar()) && c<='9'; x=(x<<1)+(x<<3)+(c^48));
	return f? -x: x;
}

const int maxn=400;

int n, mod;
int f[maxn+5][maxn+5][3];

inline void add(int& x, int delta){ x=(x+delta)%mod; }

signed main(){
	n=readin(1), mod=readin(1);
	// turn on the first list "manually"
	f[1][1][0]=1; f[1][1][1]=2;
	rep(i, 1, n-1){
		rep(j, 1, i) rep(k, 0, min(2, j)){
			// have nothing to do with any segment
			add(f[i+1][j+1][k], 1ll*f[i][j][k]*(j+1-k)%mod);

			// stand alone on the edge
			if(k<2) add(f[i+1][j+1][k+1], 1ll*f[i][j][k]*(2-k)%mod);

			// stand by a segment
			// just close
			add(f[i+1][j][k], 1ll*f[i][j][k]*((j<<1)-k)%mod);
			// pull another light into the water
			add(f[i+2][j][k], 1ll*f[i][j][k]*((j<<1)-k)%mod);

			// connect the two segments
			// two closed light
			add(f[i+2][j-1][k], 1ll*f[i][j][k]*(j-1)*2%mod);
			// three closed light
			add(f[i+3][j-1][k], 1ll*f[i][j][k]*(j-1)%mod);

			// connect an edge with a segment
			if(k<2) add(f[i+1][j][k+1], 1ll*f[i][j][k]*(2-k)%mod);
			// pull another light into the water
			if(k<2) add(f[i+2][j][k+1], 1ll*f[i][j][k]*(2-k)%mod);
		}
	}
	printf("%d\n", f[n][1][2]);
	return 0;
}

肆、用到 の Trick ¶

为什么遇到一段一段的这种题没有往插头 \(\tt DP\) 想呢?可能是题做少了吧,也有可能是自己太菜了......

这里有一道之前做过的插头 \(\tt DP\) 的题:传送门

posted @ 2021-05-03 20:00  Arextre  阅读(158)  评论(0编辑  收藏  举报