CF1093F Vasya and Array

这个题还是有点东西的啊。
现场并没有过掉qwq
发现颜色种类很少,考虑n*k的dp。
dp[i][j]表示第i位填j的方案数。
转移的时候,枚举这个颜色段向左扩展了多长。
因为受到len的限制,它最多只能扩展到i-len+2。
这里还要预处理一下g[i][j]表示这个颜色向左最多能扩展到什么位置。
这个是为了计算颜色对扩展位置的限制,综上,t=max(g[i][j],i-len+2)。
考虑在t右边的(包括t)的位置怎么转移,发现可以把一个-1作为划分点。
这个位置以后全部与j相同。
此时这个位置只需要填任意一个与j不同的颜色即可。
这个式子显然可以用前缀和优化一波。
维护一下总方案数的前缀和,维护一下每种颜色的前缀和,转移时作差即可。
此外,还有一种情况需要注意,t-1也是可以进行转移,也是只需要填一个与j不同的颜色即可。
注意处理一下t-1为0的情况。

#include<iostream>
#include<cctype>
#include<cstdio>
#include<cstring>
#include<string>
#include<cmath>
#include<ctime>
#include<cstdlib>
#include<algorithm>
#define K 105
#define N 100050
#define L 100000
#define eps 1e-7
#define inf 1e9+7
#define ll long long
using namespace std;
inline ll read()
{
	char ch=0;
	ll x=0,flag=1;
	while(!isdigit(ch)){ch=getchar();if(ch=='-')flag=-1;}
	while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-'0';ch=getchar();}
	return x*flag;
}
const ll mo=998244353;
ll a[N],s[N],f[N][K],g[N][K],dp[N][K];
int main()
{
	ll n=read(),k=read(),len=read();
	if(len==1){printf("0");return 0;} 
	for(ll i=1;i<=n;i++)a[i]=read();
	for(ll i=1;i<=n;i++)for(ll j=1;j<=k;j++)
	if(a[i-1]==-1||a[i-1]==j)g[i][j]=g[i-1][j];
	else g[i][j]=i;
	s[0]=1;
	for(ll i=1;i<=n;i++)
	{
		for(ll j=1;j<=k;j++)if(a[i]==-1||a[i]==j)
		{
			ll t=max(g[i][j],i-len+2); 
			dp[i][j]=((s[i-1]-s[t-1])-(f[i-1][j]-f[t-1][j]))%mo;
			dp[i][j]=(dp[i][j]+s[t-1]-(t==1?0:s[t-2])-dp[t-1][j])%mo;
		}
		s[i]=s[i-1];
		for(ll j=1;j<=k;j++)
		{
			s[i]=(s[i]+dp[i][j])%mo;
			f[i][j]=(f[i-1][j]+dp[i][j])%mo;
		}
	}
	ll ans=0;
	for(ll i=1;i<=k;i++)ans=(ans+dp[n][i])%mo;
	printf("%lld",(ans+mo)%mo);	
	return 0;
}
原文地址:https://www.cnblogs.com/Creed-qwq/p/10147141.html