Codeforces Global Round 14 E. Phoenix and Computers(组合数,dp)

Posted issue是fw

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Codeforces Global Round 14 E. Phoenix and Computers(组合数,dp)相关的知识,希望对你有一定的参考价值。

LINK

灯=电脑

考虑最后的点灯状态,大概是一段连续的灯被手动打开,一个被动打开,一段连续的灯被打开…

再单独考虑打开一段连续长 k k k的灯是怎么做到的

由于不能使任何一盏灯被动打开,唯一的策略是先打开其中的任意一盏灯,之后每次都选择左边和右边任意一盏没打开的灯去打开

于是方案数为 2 k − 1 2^{k-1} 2k1

那么定义 f [ i ] [ j ] f[i][j] f[i][j]表示前 i i i盏灯,手动打开了 j j j盏的方案数

f [ i ] [ j ] = ∑ k = 1 i − j f [ i − k − 1 ] [ j − k ] ∗ 2 k − 1 ∗ ( j k ) f[i][j]=\\sum\\limits_{k=1}^{i-j}f[i-k-1][j-k]*2^{k-1}*\\binom{j}{k} f[i][j]=k=1ijf[ik1][jk]2k1(kj)

这个转移意思是,先枚举以 j j j结尾的最后一个连续段长为 k k k

那么这一段内部的方案为 2 k − 1 2^{k-1} 2k1,之前部分内部的方案为 f [ i − k − 1 ] [ j − k ] f[i-k-1][j-k] f[ik1][jk]

不过最后打开的这 k k k盏灯可以在任意时候被打开,也就是从 j j j个位置选 k k k个即可

#include <bits/stdc++.h>
using namespace std;
const int maxn = 409;
int n,mod,f[maxn][maxn],C[maxn][maxn],base[maxn];
void init()
{
	base[0] = 1; C[0][0] = 1;
	for(int i=1;i<=n;i++)	base[i] = 1ll*base[i-1]*2%mod;
	for(int i=1;i<=n;i++)
	{
		C[i][0] = 1;
		for(int j=1;j<=i;j++)
			C[i][j] = ( 1ll*C[i-1][j-1]+C[i-1][j] )%mod;
	}
}
void upd(int &x,int y){ x = ( x+y )%mod; }
int main()
{
	cin >> n >> mod;
	init();
	f[0][0] = 1;
	for(int i=1;i<=n;i++)
	{
		f[i][i] = base[i-1];
		for(int j=1;j<i;j++)//枚举前i个手动打开j个
		{
			for(int k=1;k<j;k++)//最后一段连续打开k个
				upd( f[i][j],1ll*f[i-k-1][j-k]*base[k-1]%mod*C[j][k]%mod );	
		}
	}
	int ans = 0;
	for(int i=1;i<=n;i++)	upd( ans,f[n][i] );
	cout << ans;
} 

以上是关于Codeforces Global Round 14 E. Phoenix and Computers(组合数,dp)的主要内容,如果未能解决你的问题,请参考以下文章

Codeforces Global Round 1

Codeforces Global Round 1

Codeforces Global Round 1

Codeforces Global Round 1

Codeforces Global Round 19

Codeforces Global Round 1 AParity