CodeForces 1093F Vasya and Array
题意
给一个长度为 \(n\) 的整数序列 \(a\),其中 \(a_i\) 要么为 \(-1\),要么为 \(1\sim k\) 中的整数。
求出将所有 \(-1\) 替换为 \(1\sim k\) 中整数的方案数,满足替换后的序列中不存在连续 \(l\) 个相同的数,对 \(998244353\) 取模。
\(\texttt{Data Range:}1\leq l\leq n\leq 10^5,1\leq k\leq 100\)
题解
注意到 \(k\) 的范围很小,可以设一个 \(f_{i,j}\) 表示第 \(i\) 个位置的数字为 \(j\) 的时候前缀的方案数,再记一个 \(s_i=\sum f_{i,j}\)。
首先注意到只有 \(a_i=-1\) 或者 \(a_i=j\) 的时候 \(f_{i,j}\) 才可能被转移到。
于是考虑设 \(len_{i,j}\) 表示以 \(i\) 为后缀最多连续的 \(j\) 的个数。
当 \(len_{i,j}<l\) 的时候 \(f_{i,j}=s_{i-1}\)。
否则因为可以在这段后缀中填出大于等于 \(l\) 的,要减掉不合法的方案数,为 \(s_{i-l}-f_{i-l,j}\)。
代码
#include<bits/stdc++.h>
using namespace std;
typedef int ll;
typedef long long int li;
const ll MAXN=2e5+51,MOD=998244353;
ll n,kk,l,p;
ll x[MAXN],f[MAXN][101],len[MAXN][101],s[MAXN];
inline ll read()
{
register ll num=0,neg=1;
register char ch=getchar();
while(!isdigit(ch)&&ch!='-')
{
ch=getchar();
}
if(ch=='-')
{
neg=-1;
ch=getchar();
}
while(isdigit(ch))
{
num=(num<<3)+(num<<1)+(ch-'0');
ch=getchar();
}
return num*neg;
}
int main()
{
n=read(),kk=read(),l=read(),s[0]=1;
if(l==1)
{
return puts("0"),0;
}
for(register int i=1;i<=n;i++)
{
x[i]=read();
for(register int j=1;j<=kk;j++)
{
len[i][j]=len[i-1][j]+(x[i]==-1||x[i]==j);
}
}
if(x[1]==-1)
{
for(register int i=1;i<=kk;i++)
{
f[1][i]=1;
}
s[1]=kk;
}
else
{
f[1][x[1]]=1,s[1]=1;
}
for(register int i=2;i<=n;i++)
{
for(register int j=1;j<=kk;j++)
{
if(x[i]==-1||x[i]==j)
{
f[i][j]=s[i-1];
if(i>=l)
{
p=i-l;
if(len[i][j]-len[p][j]==l)
{
f[i][j]=((li)f[i][j]+f[p][j]-s[p]+MOD)%MOD;
}
}
}
s[i]=(s[i]+f[i][j])%MOD;
}
}
printf("%d\n",s[n]);
}