Examples
input
2 3
-1 -1
output
9
input
5 2
1 -1 -1 1 2
output
0
input
4 200000
-1 -1 12345 -1
output
735945883
题意
给出一个长为nnn的数组,数组中的每个元素要么是111$k$要么是$-1$,任务是统计将所有的$-1$用$1$kkk的数填入之后不产生长为奇数的回文串的填法之和 mod 998244353mod~998244353mod 998244353的值。
输入第一行为nnn和kkk,第二行为数组。
思路
这题我当时没做出来,甚至一点头绪都没有
注意到题目只限制了长为奇数的回文串不得出现,而长为5的回文串只能由长为3的回文串两边各添加一个相同元素得到,故只要不出现长为3的回文串即可。
不出现长为3的回文串说明对于任意iii有a[i−2]!=a[i]a[i-2]!=a[i]a[i−2]!=a[i]且a[i]!=a[i+2]a[i]!=a[i+2]a[i]!=a[i+2](如果存在)。
这样就可以把a[]a[]a[]分成a[i]a[i]a[i](i为奇数)和a[i]a[i]a[i](i为偶数)两个数列讨论,要求则变为数列中任两个相邻元素不得相同。
- (然后疯狂分类讨论)
考虑每个连续的全为−1-1−1的区间,有如下几种情况:
- 这个区间两边都没有已填进的数字,显然这个区间的填法总数为k∗(k−1)k*(k-1)k∗(k−1)(区间长度-1)
- 这个区间左边有已填进的数字,右边没有已填进的数字。这时左起第一位有(k−1)(k-1)(k−1)种填法,在填入该位后,后一位同样有(k−1)(k-1)(k−1)种填法,以此类推……总填法数为(k−1)(k-1)(k−1)(区间长度)
- 这个区间右边有已填进的数字,左边没有已填进的数字。这时右起第一位有(k−1)(k-1)(k−1)种填法,在填入该位后,前一位同样有(k−1)(k-1)(k−1)种填法,以此类推……总填法数为(k−1)(k-1)(k−1)(区间长度)
- 这个区间左边及右边都有已填进的数字,但两边已填进的数字相同。这时我们可以设dp[i][0]dp[i][0]dp[i][0]为第iii位填入的数字与左右两边已填进的数字不同时填法总数,dp[i][1]dp[i][1]dp[i][1]为第iii位填入的数字与左右两边已填进的数字相同时填法总数。可得动态转移方程dp[i][0]=(dp[i−1][0]∗(max(k−2,0))+dp[i−1][1]∗max(k−1,0))dp[i][0]=(dp[i-1][0]*(max(k-2,0))+dp[i-1][1]*max(k-1,0))dp[i][0]=(dp[i−1][0]∗(max(k−2,0))+dp[i−1][1]∗max(k−1,0))
dp[i][1]=(dp[i−1][0])dp[i][1]=(dp[i-1][0])dp[i][1]=(dp[i−1][0])
显然dp[1][0]=k−1dp[1][0]=k-1dp[1][0]=k−1,dp[1][1]=0dp[1][1]=0dp[1][1]=0。填法总数为dp[区间长度][0]dp[区间长度][0]dp[区间长度][0] - 这个区间左边及右边都有已填进的数字,且两边已填进的数字不同。这时我们可以设dp[i][0]dp[i][0]dp[i][0]为第iii位填入的数字与左右两边已填进的数字均不同时填法总数,dp[i][1]dp[i][1]dp[i][1]为第iii位填入的数字与左边已填进的数字相同时填法总数。dp[i][2]dp[i][2]dp[i][2]为第iii位填入的数字与右边已填进的数字相同时填法总数。可得动态转移方程dp[i][0]=(dp[i−1][0]∗(max(k−3,0))+dp[i−1][1]∗max(k−2,0)+dp[i−1][2]∗max(k−2,0))dp[i][0]=(dp[i-1][0]*(max(k-3,0))+dp[i-1][1]*max(k-2,0)+dp[i-1][2]*max(k-2,0))dp[i][0]=(dp[i−1][0]∗(max(k−3,0))+dp[i−1][1]∗max(k−2,0)+dp[i−1][2]∗max(k−2,0))dp[i][1]=(dp[i−1][0]+dp[i−1][2])dp[i][1]=(dp[i-1][0]+dp[i-1][2])dp[i][1]=(dp[i−1][0]+dp[i−1][2])dp[i][2]=(dp[i−1][0]+dp[i−1][1])dp[i][2]=(dp[i-1][0]+dp[i-1][1])dp[i][2]=(dp[i−1][0]+dp[i−1][1])
显然dp[1][0]=k−2dp[1][0]=k-2dp[1][0]=k−2,dp[1][1]=0dp[1][1]=0dp[1][1]=0,dp[1][2]=1dp[1][2]=1dp[1][2]=1。填法总数为dp[区间长度][0]+dp[区间长度][1]dp[区间长度][0]+dp[区间长度][1]dp[区间长度][0]+dp[区间长度][1] - 最后还有一个坑,有可能拆分成两个数列后,数列中已经有相邻的相同且不为−1-1−1的元素,这种情况需要特判。
下面AC代码:
#include<stdio.h>
#define max(a,b) ((a)>(b)?(a):(b))
using namespace std;
const long long MOD=998244353;
long long a[2][200005];
long long dp[200005][3];
long long n,k,ans=1;
long long right(long long l,long long r){if(l>r)return 1;long long ans=k-1;while(r>l){r--;ans*=(k-1);ans%=MOD;}return ans;}
long long left(long long l,long long r){if(l>r)return 1;long long ans=k-1;while(r>l){l++;ans*=(k-1);ans%=MOD;}return ans;}
long long full(long long l,long long r){if(l>r)return 1;long long ans=k;while(r>l){l++;ans*=(k-1);ans%=MOD;}return ans;}
long long m(long long l,long long r,long long a[])
{
if(l==r-1)if (a[l]!=a[r])return 1;else return 0;
if(a[l]!=a[r])
{
dp[l+1][0]=k-2;//不与两边相同
dp[l+1][1]=0;//与左边相同
dp[l+1][2]=1;//与右边相同
for(long long i=l+2;i<r;i++)
{
dp[i][0]=(dp[i-1][0]*(max(k-3,0))+dp[i-1][1]*max(k-2,0)+dp[i-1][2]*max(k-2,0))%MOD;
dp[i][1]=(dp[i-1][0]+dp[i-1][2])%MOD;
dp[i][2]=(dp[i-1][0]+dp[i-1][1])%MOD;
}
return(dp[r-1][0]+dp[r-1][1])%MOD;
}else
{
dp[l+1][0]=k-1;//不与两边相同
dp[l+1][1]=0;//与左边相同
for(long long i=l+2;i<r;i++)
{
dp[i][0]=(dp[i-1][0]*(max(k-2,0))+dp[i-1][1]*max(k-1,0))%MOD;
dp[i][1]=(dp[i-1][0])%MOD;
}
return dp[r-1][0]%MOD;
}
}
int main()
{
scanf("%lld%lld",&n,&k);
for(long long i=1;i<=n;i++) scanf("%lld",&a[i%2][(i-1)/2+1]);
long long f1=n/2+n%2,f2=n/2;
long long i,j;
for(i=1;i<=f1;)
{
for(j=i+1;j<=f1&&a[1][j]==-1;j++);
if(j>f1)
{
if(a[1][i]==-1)
{
ans*=full(1,f1);
ans%=MOD;
}else
{
ans*=left(i+1,j-1);
ans%=MOD;
}
}
else
{
if(a[1][i]==-1)
{
ans*=right(i,j-1);
ans%=MOD;
}else
{
ans*=m(i,j,a[1]);
ans%=MOD;
}
}
i=j;
}
for(i=1;i<=f2;)
{
for(j=i+1;j<=f2&&a[0][j]==-1;j++);
if(j>f2)
{
if(a[0][i]==-1)
{
ans*=full(1,f2);
ans%=MOD;
}else
{
ans*=left(i+1,j-1);
ans%=MOD;
}
}
else
{
if(a[0][i]==-1)
{
ans*=right(i,j-1);
ans%=MOD;
}else
{
ans*=m(i,j,a[0]);
ans%=MOD;
}
}
i=j;
}
printf("%lld",ans);
return 0;
}