题目大意:
有一个大小为n
(
<
=
2000
)
(<=2000)
(<=2000)的h序列,然后问有多少个ans序列将其向右移一位之后(n移到1),与h序列的匹配数比原先多,对答案mod 998244353
题目链接:https://codeforces.ml/problemset/problem/1227/F1
解题思路:
考虑每个位置对下一个位置的贡献:
- 符合当前位置的选择时,且符合下一个位置,贡献为0
- 符合当前位置的选择时,不符合下一个位置,贡献为-1
- 不符合当前位置的选择时,且不符合下一个位置,贡献为0
- 不符合当前位置的选择时,且符合下一个位置,贡献为1
因此当最后一个位置,总贡献大于0的个数,即为答案个数
先设tmp1为当前位置贡献为1的个数,tmp2为当前位置贡献为0的个数,tmp3为贡献-1的个数
状态转移方程:
s
u
m
[
i
]
[
j
]
=
s
u
m
[
i
−
1
]
[
j
−
1
]
∗
t
m
p
1
+
s
u
m
[
i
−
1
]
[
j
]
∗
t
m
p
2
+
s
u
m
[
i
−
1
]
[
j
+
1
]
∗
t
m
p
3
sum[i][j]=sum[i-1][j-1]*tmp1+sum[i-1][j]*tmp2+sum[i-1][j+1]*tmp3
sum[i][j]=sum[i−1][j−1]∗tmp1+sum[i−1][j]∗tmp2+sum[i−1][j+1]∗tmp3
注:即使一开始贡献全为负数,但后面仍然可能为正,因此从2000开始代表当前位置贡献为0
AC代码:
#include<bits/stdc++.h>
#define Mod 998244353
using namespace std;
typedef long long ll;
ll n,k,ans,h[201000],sum[2][401000];//这里加个滚动数组,可优化内存
int main() {
scanf("%lld%lld",&n,&k);
for(int i=1; i<=n; i++) scanf("%lld",&h[i]);
if(n==1) {
printf("0\n");
return 0;
}
if(h[1]==h[2]) sum[1][1999]=0,sum[1][2000]=k,sum[1][2001]=0;
else sum[1][1999]=1,sum[1][2000]=k-2,sum[1][2001]=1;//初始化,根据第一个位置以及下一个位置的值
for(int i=2; i<=n; i++) {
ll x=h[i],y=h[i%n+1];
ll tmp1=0,tmp2=0,tmp3=0;//分别为贡献可为1的个数,0的个数,-1的个数
if(x==y) tmp1=0,tmp2=k%Mod,tmp3=0;//先计算出该位置等于下一个位置的各个tmp的值
else tmp1=1,tmp2=(k-2)%Mod,tmp3=1;
for(int j=0; j<=4000; j++) {
if(j==0) sum[i%2][j]=(tmp2*sum[(i-1)%2][j]%Mod+tmp3*sum[(i-1)%2][j+1]%Mod)%Mod;
else sum[i%2][j]=(tmp1*sum[(i-1)%2][j-1]%Mod+tmp2*sum[(i-1)%2][j]%Mod+tmp3*sum[(i-1)%2][j+1]%Mod)%Mod;
}
}
for(int i=2001; i<=4000; i++)//2001开始才为贡献为正
if(sum[n%2][i])
ans=(ans+sum[n%2][i])%Mod;
printf("%lld\n",ans);
}