【Codeforces】EDU Round 140 (Div. 2) C. Count Binary Strings | 动态规划

205 阅读2分钟

开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第22天,点击查看活动详情

题目链接

Problem - C - Codeforces

题目

image.png

题目大意

统计符合要求的长度为 nn0101S={s1,s2,...,sn}S=\{s_1,s_2,...,s_n\} 的数量。

要求表现为一个 n×nn\times n 的矩阵,其中 a[i][j]a[i][j] 表示:

  • i>ji>j,则无意义。
  • a[i][j]=1a[i][j]=1,则 {si,si+1,sj}\{s_i,s_{i+1},s_j\} 包含 11 种字符。
  • a[i][j]=2a[i][j]=2,则 {si,si+1,sj}\{s_i,s_{i+1},s_j\} 包含 22 种字符。
  • a[i][j]=0a[i][j]=0,则不确定 {si,si+1,sj}\{s_i,s_{i+1},s_j\} 包含几种字符。

思路

为了方便描述,定义 s[l,r]s[l,r] 表示以 llrr 为左右端点的字符串 {sl,sl+1,...,r}\{s_l,s_{l+1},...,r\}

思考

首先很容易发现:

  • 如果 a[l][r]=1a[l][r]=1,满足 lxyrl\le x\le y \le r 的区间 s[x,y]s[x,y] 都应该包含 11 种字符。
  • 如果 a[l][r]=2a[l][r]=2,满足 xlryx\le l\le r \le y 的区间 s[x,y]s[x,y] 都应该包含 22 种字符。

通过上述规则,我们将输入的要求矩阵进行改写。如果需要将 11 修改为 22,显然方案数为 00。将 22 修改成 11 同理。

将矩阵进行改写后,令 dp[i][j]dp[i][j] 表示前 ii 个位置,其中后 jj 个位置填的字符相同的方案数。方程为

for (int i=1;i<n;++i)
    for (int j=1;j<=i;++j)
    {
        if (a[i-j+1][i+1]!=2) dp[i+1][j+1]=(dp[i+1][j+1]+dp[i][j])%mod;//一样 
        if (a[i][i+1]!=1) dp[i+1][1]=(dp[i+1][1]+dp[i][j])%mod;//不一样
    }

下面进行解析。假设我们已经求解出了前 ii 个位置的 dpdp 数组,欲填充第 i+1i+1 个位置。枚举其中后 jj 个位置填的字符相同,则:

  • 如果 a[ij+1][i+1]2a[i-j+1][i+1]\neq 2,则 si+1s_{i+1} 可以等于 sis_i,转移为
dp[i+1][j+1]=dp[i+1][j+1]+dp[i][j]dp[i+1][j+1]=dp[i+1][j+1]+dp[i][j]

让我们论证这一步转移出来的是合法方案:

  1. 当我们求解到第 i+1i+1 个位置,那么以 1 i1~i 为右端点的要求一定已经被满足了。
  2. 对于以 i+1i+1 为右端点的需求:由状态的定义可知 s[ij+1,i]s[i-j+1,i] 中元素全部相同,让第 i+1i+1 个位置与前一个位置相同,则 s[ij+1,i+1]s[i-j+1,i+1] 中元素全部相同,符合 a[ij+1][i]a[i-j+1][i]0011;对于 x<ij+1x<i-j+1a[x][i+1]a[x][i+1] 一定不等于 11,合法;对于 ij+1<xii-j+1<x\le ia[x][i+1]a[x][i+1] 一定不等于 22,合法。

所以以 1~i+1 为右端点的要求都被满足了。

  • 如果 a[i][i+1]1a[i][i+1]\neq 1,则 si+1s_{i+1} 可以不等于 sis_i,转移为
dp[i+1][1]=dp[i+1][1]+dp[i][j]dp[i+1][1]=dp[i+1][1]+dp[i][j]

让我们论证这一步转移出来的是合法方案:

如果 a[i][i+1]1a[i][i+1]\neq 1,说明没有区间 x<ix<i 使得 a[x][i+1]=1a[x][i+1]=1,所以合法。

最后对前 nn 个位置末尾相同元素数量不同的方案求和即可。

注意取余。

代码

代码写得很不优雅,但是 AC 了就懒得改了……

#include <stdio.h>
#include <algorithm>
using namespace std;
using LL=long long;
const int N=1e2+5;
const LL mod=998244353;
int n,m,k,a[N][N],lst[N];
LL dp[N][N];
LL solve()
{
	scanf("%d",&n);
	for (int i=1;i<=n;++i) lst[i]=i;
	for (int i=1;i<=n;++i)
		for (int j=i;j<=n;++j)
			if (scanf("%d",&a[i][j]),a[i][j]==1) lst[i]=j;
	for (int i=2;i<=n;++i) lst[i]=max(lst[i-1],lst[i]);
	for (int i=1;i<=n;++i)
		for (int j=i;j<=n;++j)
		{
			if (j>lst[i]) break;
			if (a[i][j]==2) return 0;
			a[i][j]=1;
		}	
	for (int dt=0;dt<n;++dt)
		for (int i=1;i+dt<=n;++i)
		{
			if (a[i][i+dt]!=2) continue;
			if (i+dt+1<=n) a[i][i+dt+1]=2;
			if (i-1>0) a[i-1][i+dt]=2;
		}
	dp[1][1]=2;
	for (int i=1;i<n;++i)
	{
		for (int j=1;j<=i;++j)
		{
			if (a[i-j+1][i+1]!=2) dp[i+1][j+1]=(dp[i+1][j+1]+dp[i][j])%mod;//一样 
			if (a[i][i+1]!=1) dp[i+1][1]=(dp[i+1][1]+dp[i][j])%mod;//不一样 
		}
	}
	LL ans=0;
	for (int i=1;i<=n;++i) ans=(ans+dp[n][i])%mod;
	return ans;
		
}
int main()
{
	int T=1;
	while (T--) printf("%lld\n",solve());
	return 0;
}