题意
给一个长度为 (n) 的整数序列 (a),其中 (a_i) 要么为 (-1),要么为 (1sim k) 中的整数。
求出将所有 (-1) 替换为 (1sim k) 中整数的方案数,满足替换后的序列中不存在连续 (l) 个相同的数,对 (998244353) 取模。
( exttt{Data Range:}1leq lleq nleq 10^5,1leq kleq 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
",s[n]);
}