题目链接
题意
给你一个长为 n n n 的数组,和一个值 k k k,你能改变 − 1 -1 −1 为 1 − k 1-k 1−k 中的任意值,求字串不是回文串(长度大于 1 1 1,且长度为奇数)的方案数,膜 998244353 998244353 998244353
思路
- 第一步比较好想,如果一个串满足上述回文串,那么其长度为3的中心字串必定回文,所以只要使所有长度为3的子串不是回文串即满足。
- 进一步简化,存在回文串,等价于,存在 i i i 满足 a i = = a i − 2 ∣ ∣ a i = = a i + 2 a_i == a_{i-2} || a_i == a_{i+2} ai==ai−2∣∣ai==ai+2 ,那么我们可以奇偶分离判断相邻数字是否相同,最后两组方案数相乘即可,接下来对奇偶分离后的组进行分析,设长度为 n n n,能选的数一共 k k k 种。
- 将相邻的
−
1
-1
−1 全部提取一块计算,将所有块相乘即答案,单个块设存在
l
e
n
len
len 个
−
1
-1
−1
- 全部为-1,即首位 k k k 种选择,其余全部 k − 1 k-1 k−1 种选择,即 a n s = k ∗ ( k − 1 ) n − 1 ans = k*(k-1)^{n-1} ans=k∗(k−1)n−1。
- 单方向限制,左边或右边没有限制, a n s = ( k − 1 ) l e n ans = (k-1)^{len} ans=(k−1)len
- 数字两边不同、数字两边相同,预处处理 d p [ 是 否 相 同 ] [ 长 度 ] dp[是否相同][长度] dp[是否相同][长度],详见代码 d p [ 0 ] dp[0] dp[0] 表示相同, d p [ 1 ] dp[1] dp[1] 表示不同
代码
#include <bits/stdc++.h>
using namespace std;
#define ll long long
ll read(ll &a){return scanf("%lld",&a);}
ll rd(){ll tmp; scanf("%lld",&tmp); return tmp;}
ll mod = 998244353;
ll dp[2][200005], v[200005], m1[200005], tot1 = 0, m2[200005], tot2 = 0, n, k;
ll qpow(ll a, ll b)
{
ll tmp = 1;
while(b)
{
if(b&1) tmp = tmp*a%mod;
a = a*a%mod;
b >>= 1;
}
return tmp;
}
ll f(ll wtf[], ll len)
{
for(ll i = 2; i <= len; ++i) if(wtf[i] == wtf[i-1] && wtf[i] != -1) return 0ll;
ll le = 0, ans = 1;
for(ll i = 1; i <= len; ++i)
{
if(wtf[i] != -1 || i == len)
{
if(i == len && wtf[i] == -1) ++i;
if(le == 0 && i == len+1) return k*qpow(k-1,len-1)%mod;
if(le == 0 || i == len+1) ans = ans*qpow(k-1,i-1-le)%mod;
else if(wtf[le] == wtf[i]) ans = ans*dp[0][i-le-1]%mod;
else ans = ans*dp[1][i-le-1]%mod;
le = i;
}
}
return ans;
}
int main()
{
n = rd(), k = rd();
for(ll i = 1; i <= n; ++i) read(v[i]);
dp[0][0] = dp[1][0] = 1;
for(ll i = 1; i <= n; ++i)
{
dp[0][i] = (k-1)*dp[1][i-1]%mod;
dp[1][i] = ((i > 1ll ? dp[0][i-1] : 0)+max(0ll,k-2)*dp[1][i-1]%mod)%mod;
}
for(ll i = 1; i <= n; i += 2) m1[++tot1] = v[i];
for(ll i = 2; i <= n; i += 2) m2[++tot2] = v[i];
printf("%lld\n",f(m1,tot1)*f(m2,tot2)%mod);
return 0;
}