AGC050C Block Game

考虑第一次操作,必然会放在当前 Snuke 的任意一边。

然后考虑第二次操作,必然放在当前 Snuke 的另一边。

然后现在 Snuke 就被圈起来了,仔细考虑一下会发现,它的决策必然是往尽量中间的位置靠。因为在下一次放方块的操作中,你肯定会让 Snuke 的活动范围尽量减少。也就是说,设你放方块的时候 Snuke 到左边长度是 ll ,到右边是 rr ,那么放完后他的范围肯定是变为 min(l,r)\min(l,r)

我们考虑什么样的 B 操作,会导致能确定之后必然能堵死 Snuke 。对于最后两次放块,显然当且仅当这两次 B 连着的时候,Snuke 必定被堵住,如果在最后一次之前 Snuke 已经在一个范围为 22 内左右横跳了,那么说明之前就已经被确定能堵死 Snuke 了。因为在倒数第二次放块操作时,如果可以把 Snuke 确定在一个 22 的范围内,也就是距离上一次 B 操作仅隔了一个 S ,这样可以肯定之后无论进行多少次 S ,Snuke 都一定会被堵死。然后考虑倒数第三次放块,会发现和倒数第四次隔了 33S ,倒数第四次是 77 ,理性分析一下会发现是 2k112^{k-1}-1 。具体过程画一下图即可。

于是就可以 dpdp 了。设 dp[i][k]dp[i][k] 表示当前从右到左考虑到第 ii 个位置,到现在一共放了 kk 个方块即可。转移 O(1)O(1) ,总复杂度 O(nlogn)O(n\log n)

#include "iostream"
#include "algorithm"
#include "cstring"
#include "cstdio"
#include "cmath"
#include "vector"
#include "map"
#include "set"
#include "queue"
using namespace std;
#define MAXN 1000006
//#define int long long
#define rep(i, a, b) for (int i = (a), i##end = (b); i <= i##end; ++i)
#define per(i, a, b) for (int i = (a), i##end = (b); i >= i##end; --i)
#define pii pair<int,int>
#define fi first
#define se second
#define mp make_pair
#define pb push_back
#define eb emplace_back
#define vi vector<int>
#define all(x) (x).begin() , (x).end()
#define mem( a ) memset( a , 0 , sizeof a )
typedef long long ll;
const int P = 998244353;
int n , m;
char s[MAXN];

int dp[MAXN][25] , S[MAXN] , qm = 0 , bm = 0;

void solve() {
	scanf("%s",s + 1);
	n = strlen( s + 1 );
	rep( i , 1 , n ) S[i] = S[i - 1] + ( s[i] == 'B' );
	int flg = 0;
	dp[n + 1][0] = 1;
	per( i , n , 1 ) {
		qm += s[i] == '?';
		bm += s[i] == 'B';
		if( s[i] != 'B' ) dp[i][0] = dp[i + 1][0];
		if( s[i] != 'S' ) dp[i][1] = dp[i + 1][0];
		rep( k , 1 , 22 ) {
			if( k > 1 && i + ( 1 << k - 2 ) + 1 > n ) break;
			if( s[i] != 'B' ) 
				dp[i][k] = ( dp[i][k] + dp[i + 1][k] ) % P;
			if( k > 1 && s[i] != 'S' && S[i + ( 1 << k - 2 )] - S[i] == 0 )
				dp[i][k] = ( dp[i][k] + dp[i + ( 1 << k - 2 ) + 1][k - 1] ) % P;
		}
	}
	int as = 1;
	rep( i , 1 , qm ) as = as * 2 % P;
	rep( k , 0 , 22 ) as = ( as + P - dp[1][k] ) % P;
	cout << as << endl;
}

signed main() {
//    int T;cin >> T;while( T-- ) solve();
    solve();
}


\