【CF1487G】String Counting
题目
题目链接:https://codeforces.com/contest/1487/problem/G
你有 \(26\) 个不同的字符,第 \(i\) 个字符有 \(c_i\) 个。
你希望用这些字符,构造出一个字符串(每个字符在字符串中出现的个数不超过 \(c_i\)),使得这个字符串上不存在长度为奇数且大于 \(1\) 的回文串。求出方案数对 \(998244353\) 取模的结果。
\(n\leq 400;\frac{n}{3} < c_i \leq n\)。
思路
显然等价于不存在两个位置 \(i,i+2\) 满足 \(s_i=s_{i+2}\)。
这个 \(c_i>\frac{n}{3}\) 意味着最多只有两个字符可能会超出限制,所以考虑容斥。
设 \(f[i][j][k][0/1/2][0/1/2]\) 表示选到第 \(i\) 个位置,字符 \(x\) 出现 \(j\) 次,\(y\) 出现 \(k\) 次,第 \(i-2,i-1\) 位置的字符分别为 除 \(x,y\) 外的字符/字符 \(x\)/字符 \(y\) 的方案数。
不用考虑字符 \(x,y\) 分别是什么,也不用考虑使用有没有超出 \(c_x,c_y\),只要求不存在两个位置 \(i,i+2\) 满足 \(s_i=s_{i+2}\)。
这个东西大力分类讨论即可。注意当第 \(i\) 位填除 \(x,y\) 外的字符时,转移过来的权值可能为 \(23\) 或 \(24\)。
然后记 \(g[i][j]\) 表示字符 \(x\) 使用至少 \(i\) 个,字符 \(y\) 使用至少 \(j\) 个的方案数。直接把 \(f\) 做一遍后缀和即可。
容斥一下即可得到答案
把 \(f\) 数组滚动一下,时间复杂度 \(O(n^3)\),空间复杂度 \(O(n^2)\)。
代码
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=210,MOD=998244353;
int n,ans,c[30],f[2][N][N][3][3],g[N][N];
signed main()
{
scanf("%d",&n);
for (int i=1;i<=26;i++)
scanf("%d",&c[i]);
f[0][0][0][0][0]=24*24;
f[0][1][0][1][0]=f[0][1][0][0][1]=f[0][0][1][0][2]=f[0][0][1][2][0]=24;
f[0][1][1][1][2]=f[0][1][1][2][1]=f[0][2][0][1][1]=f[0][0][2][2][2]=1;
for (int i=3;i<=n;i++)
{
int id=i&1;
memset(f[id],0,sizeof(f[id]));
for (int j=0;j<=n/2+1;j++)
for (int k=0;k<=n/2+1;k++)
{
f[id][j][k][0][0]=(23LL*f[id^1][j][k][0][0]+24LL*f[id^1][j][k][1][0]+24LL*f[id^1][j][k][2][0])%MOD;
f[id][j][k][1][0]=(23LL*f[id^1][j][k][0][1]+24LL*f[id^1][j][k][1][1]+24LL*f[id^1][j][k][2][1])%MOD;
f[id][j][k][2][0]=(23LL*f[id^1][j][k][0][2]+24LL*f[id^1][j][k][1][2]+24LL*f[id^1][j][k][2][2])%MOD;
if (j) f[id][j][k][0][1]=(f[id^1][j-1][k][0][0]+f[id^1][j-1][k][2][0])%MOD;
if (j) f[id][j][k][1][1]=(f[id^1][j-1][k][0][1]+f[id^1][j-1][k][2][1])%MOD;
if (j) f[id][j][k][2][1]=(f[id^1][j-1][k][0][2]+f[id^1][j-1][k][2][2])%MOD;
if (k) f[id][j][k][0][2]=(f[id^1][j][k-1][0][0]+f[id^1][j][k-1][1][0])%MOD;
if (k) f[id][j][k][1][2]=(f[id^1][j][k-1][0][1]+f[id^1][j][k-1][1][1])%MOD;
if (k) f[id][j][k][2][2]=(f[id^1][j][k-1][0][2]+f[id^1][j][k-1][1][2])%MOD;
}
}
for (int i=n/2+1;i>=0;i--)
for (int j=n/2+1;j>=0;j--)
{
ll sum=0;
for (int k=0;k<=8;k++)
sum=(sum+f[n&1][i][j][k/3][k%3])%MOD;
g[i][j]=(sum+g[i+1][j]+g[i][j+1]-g[i+1][j+1])%MOD;
}
ans=g[0][0];
for (int i=1;i<=26;i++)
if (c[i]+1<N) ans=(ans-g[c[i]+1][0])%MOD;
for (int i=1;i<=26;i++)
for (int j=i+1;j<=26;j++)
if (max(c[i],c[j])+1<N)
ans=(ans+g[c[i]+1][c[j]+1])%MOD;
printf("%d",(ans%MOD+MOD)%MOD);
return 0;
}