arc119f题解
arc119f
AtCoder Express 3
自动机写法。开始在做的时候题解没讲每个节点代表什么状态,自己推了一遍,记录一下。
思路
计数,求有多少种替换方式使得
可以做
观察发现,在
考虑建一个自动机来求出任意状态对应的下一个状态。由于
-
状态
0 。当前在A ,上一个位置为B ,记为\text{A} 。 -
状态
1 即状态0 反过来,记为\text{B} 。 -
状态
2 。当前在A ,上一个位置为B ,下一个位置为A ,记为\text{AA} 。 -
状态
4 。当前在A ,上一个位置为B ,下两个位置为AA ,记为\text{AAA} 。 -
状态
6 。当前在A ,上一个位置为B ,下三个位置为AAA ,记为\text{AAAA} 。此时如果往后有B ,直接先后退越过;否则走向自己,不会有5 个A 的状态。注意到此时无论如何都会后退,所以稍微调整一下。定义状态6 为当前在B ,接下来有连续A 且一定会选择跳过。 -
状态
8 。当前在A ,上一个位置不重要,下一个位置为B ,记为\text{AB} 。此时可以跳到下一个A ,也可以爬到B ,但不知道走一步后要去A 还是B 。 -
状态
10 至12 。当前既可以当作在A ,也可以当作在B ,记为\text{O} ,称为状态12 。状态10 表示当前在O ,下一个为A ,记为\text{OA} 。
综上,有
-
-
上面
5 种状态取反得到当前在B 的另外5 种。 -
考虑转移。记
-
状态
\text{A} 。如果加入A ,走向状态\text{AA} ,点没有移动,代价为0 。如果加入B ,走向状态\text{AB} ,点没有移动,代价为0 。 -
状态
\text{AA} 。如果加入A ,走向状态\text{AAA} ,点没有移动,代价为0 。如果加入B ,走向状态\text{AB} ,点向后移动一格,代价为1 。 -
状态
\text{AAA} 。如果加入A ,走向状态\text{AAAA} ,点后退一步到上一个B ,代价为1 。如果加入B ,可以先向后再向前越过AAA 到下一个B ,也可以爬两步A ,即走向状态\text{O} ,代价为2 。 -
状态
\text{AAAA} 。注意这里状态不同于以前,当前点为B 。如果加入A ,走向状态自己\text{AAAA} ,点不动,代价为0 。如果加入B ,向前越过AAAA 到下一个B ,即走向状态\text{B} ,代价为1 。 -
状态
\text{AB} 。如果加入A ,可以向后走到B ,也可以跳到下一个A ,即走向状态O ,代价为1 。如果加入B ,虽然没有连续4 个B ,但一定会从A 跳过这段B ,即状态\text{BBBB} ,代价为0 。 -
状态
\text{OA} 。如果加入A ,虽然没有连续4 个A ,但一定会把O 当作B 跳过这段A ,即状态\text{AAAA} ,代价为0 。如果加入B ,可以向后走到A ,也可以把O 当作B 跳到下一个B ,即走向状态\text{O} ,代价为1 。 -
状态
\text{O} 。如果加入A ,走向状态\text{OA} ,代价为0 。如果加入B ,走向状态\text{OB} ,代价为0 。
分析完自动机状态的转移,dp 即可。设
注意到加入一位并不是走到一位。状态设定的当前走到的点不是状态的最后一位。记录
复杂度
code
#include<bits/stdc++.h>
#define int long long
#define mod 1000000007
using namespace std;
const int maxn=4010;
const int inf=1e9;
inline int read(){
int x=0,f=1;
char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=(x<<3)+(x<<1)+(ch-48);ch=getchar();}
return x*f;
}
int n,m,ans;
char c[maxn];
int tree[15][2],val[15][2];
int dis[7];
int dp[maxn][maxn][15];
int T;
signed main(){
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
n=read();m=read();
scanf("%s",c+1);
tree[0][0]=2;val[0][0]=0;tree[0][1]=8;val[0][1]=0;
tree[2][0]=4;val[2][0]=0;tree[2][1]=8;val[2][1]=1;
tree[4][0]=6;val[4][0]=1;tree[4][1]=12;val[4][1]=2;
tree[6][0]=6;val[6][0]=0;tree[6][1]=1;val[6][1]=1;
tree[8][0]=12;val[8][0]=1;tree[8][1]=7;val[8][1]=0;
tree[10][0]=6;val[10][0]=0;tree[10][1]=12;val[10][1]=1;
tree[12][0]=10;val[12][0]=0;tree[12][1]=11;val[12][1]=0;
for(int i=0;i<=5;i++){//状态取反。
for(int k=0;k<=1;k++){
if(tree[i*2][k^1]==12)tree[i*2+1][k]=tree[i*2][k^1];
else tree[i*2+1][k]=tree[i*2][k^1]^1;
val[i*2+1][k]=val[i*2][k^1];
}
}
// for(int i=0;i<=12;i++)cout<<tree[i][0]<<" "<<val[i][0]<<" "<<tree[i][1]<<" "<<val[i][1]<<"\n";
dis[0]=dis[3]=dis[4]=dis[5]=dis[6]=1;
dis[1]=dis[2]=2;
dp[0][0][12]=1;
for(int i=0;i<=n-2;i++){
for(int j=0;j<=m;j++){
for(int k=0;k<=12;k++){
if(dp[i][j][k]){
if(c[i+1]=='A'){
dp[i+1][j+val[k][0]][tree[k][0]]+=dp[i][j][k];
dp[i+1][j+val[k][0]][tree[k][0]]%=mod;
}
else if(c[i+1]=='B'){
dp[i+1][j+val[k][1]][tree[k][1]]+=dp[i][j][k];
dp[i+1][j+val[k][1]][tree[k][1]]%=mod;
}
else{
dp[i+1][j+val[k][0]][tree[k][0]]+=dp[i][j][k];
dp[i+1][j+val[k][0]][tree[k][0]]%=mod;
dp[i+1][j+val[k][1]][tree[k][1]]+=dp[i][j][k];
dp[i+1][j+val[k][1]][tree[k][1]]%=mod;
}
}
}
}
}
for(int j=0;j<=m;j++){
for(int k=0;k<=12;k++)if(j+dis[k>>1]<=m){
ans+=dp[n-1][j][k];
ans%=mod;
}
}
printf("%lld\n",ans);
}