开启掘金成长之旅!这是我参与「掘金日新计划 · 12 月更文挑战」的第22天,点击查看活动详情
题目链接
题目
题目大意
统计符合要求的长度为 的 串 的数量。
要求表现为一个 的矩阵,其中 表示:
- 若 ,则无意义。
- ,则 包含 种字符。
- ,则 包含 种字符。
- ,则不确定 包含几种字符。
思路
为了方便描述,定义 表示以 和 为左右端点的字符串 。
思考
首先很容易发现:
- 如果 ,满足 的区间 都应该包含 种字符。
- 如果 ,满足 的区间 都应该包含 种字符。
通过上述规则,我们将输入的要求矩阵进行改写。如果需要将 修改为 ,显然方案数为 。将 修改成 同理。
将矩阵进行改写后,令 表示前 个位置,其中后 个位置填的字符相同的方案数。方程为
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;//不一样
}
下面进行解析。假设我们已经求解出了前 个位置的 数组,欲填充第 个位置。枚举其中后 个位置填的字符相同,则:
- 如果 ,则 可以等于 ,转移为
让我们论证这一步转移出来的是合法方案:
- 当我们求解到第 个位置,那么以 为右端点的要求一定已经被满足了。
- 对于以 为右端点的需求:由状态的定义可知 中元素全部相同,让第 个位置与前一个位置相同,则 中元素全部相同,符合 为 或 ;对于 , 一定不等于 ,合法;对于 , 一定不等于 ,合法。
所以以 1~i+1 为右端点的要求都被满足了。
- 如果 ,则 可以不等于 ,转移为
让我们论证这一步转移出来的是合法方案:
如果 ,说明没有区间 使得 ,所以合法。
最后对前 个位置末尾相同元素数量不同的方案求和即可。
注意取余。
代码
代码写得很不优雅,但是 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;
}