「UOJ748」机器人表演

题目

点这里看题目。

分析

从一开始就知道正确的思路,到最后都没有写成正确的算法

给定一个字符串 \(T\),考虑怎么验证它能不能由 \(S\) 和另外一个括号串合并起来。

一个自然的做法是,写一个 DP:设 \(f_{i,j}\) 表示 \(T[1,i]\) 能否由 \(S[1,j]\) 和另一个括号串前缀拼起来。如果我们设 \(a_i\)\(T\) 的前缀和(\(0\) 视作 \(+1\)\(1\) 视作 \(-1\)),\(b_j\)\(S\) 的前缀和,则可以得到转移:

\[\begin{aligned} f_{i+1,j+1}\leftarrow f_{i,j},&\text{ if } T[i+1]=S[j+1]\\ f_{i+1,j}\leftarrow f_{i,j},&\text{ if } a_{i+1}\ge b_j \end{aligned} \]

注意到,我们只需要维护 \(f_i\) 即可完成检查。因此我们可以做一个 DP 套 DP,复杂度为 \(O(2^tn(n+t)^2)\)(注意 \(f_i\) 只有靠后的一段是有效的)。


然而,DP 需要记录的状态还是太多了,我们最好能够舍弃状态记录直接检查。

尝试从 DP 检查变为贪心检查。贪心的思路比较纯粹,我们维护每一个 \(T[1,i]\) 前缀在“保证括号序列不会失配”的前提下,能够和 \(S\) 匹配的最大前缀。如果我们扫描完整个 \(T\) 之后,最大前缀长度为 \(|S|\)\(a_{|T|}=b_{|S|}\)\(T\) 就是合法的,否则就是非法的。

假如我们已知 \(a_i\)\(T[1,i]\) 匹配的最大长度 \(j\),考虑每次加入一个字符后的转移:

  1. 如果 \(j<|S|\)\(T[i+1]=S[j+1]\):直接匹配,转移到 \(j+1\)

  2. 如果加入 \(0\) 或者 \(a_i>b_j\):我们可以将下一个字符加入到括号序列中,转移到 \(j\)

  3. 如果加入 \(1\)\(a_i=b_j\):此时会出现失配的情况。

    假如转移到的匹配前缀长度为 \(j'\),则必须满足 \(a_i>b_{j'}\)。我们自然可以想到,转移到的状态应该是 \(\max_{j'<j,b_{j'}<a_i}j'\)

    后续还需要证明合法性和最优性:回退的过程实际上是将 \(S[j'+1,j]\) 和已有的括号序列前缀合并。原先的 \(S[j'+1,j]\) 一定是一个合法括号前缀,两个括号序列前缀合并后肯定还是一个括号序列前缀,且合并后 \(a_i>b_{j'}\) 保证了我们还可以加一个 \(1\),合法性得证。最优性比较显然。

那么我们可以做 DP,设 \(g_{i,j,k}\) 表示 \(T[1,i]\)\(S\) 的最大匹配前缀长度为 \(k\)\(a_i=j\) 的方案数,转移可以做到 \(O(n(n+t)^2)\)

Note.

  1. 实质上是建立了一个自动机,状态由 \((a,j)\) 确定,接收状态有且仅有 \((a_{|S|},|S|)\)

  2. 这样两侧限制的处理,我们通常都是以一侧为主,贪心地去满足,当另一侧限制马上要被打破的时候再调整策略,或者另一侧限制被打破后调整到合法状态

    上述贪心实际上就是以 \(S\) 的匹配为主,另一侧的括号匹配限制了 \(S\) 的匹配。优点在于,括号串合法不合法是很容易用数值检查的,但是 \(S\) 的匹配特异性很强。

    如果像我一开始的思路那样,先贪心地产生括号串的话,就不得不在“\(S\) 将要匹配不上时”调整匹配策略,不容易和 DP 一起维护。不清楚“回退”的思路在这个条件下起不起效,我怀疑可能是没有作用的。

    所以问题出在两点:

    1. 没有把握“主要矛盾”,主次把握得不好。

    2. 一般方法不熟悉,实际上我只想到了“在限制将要打破时调整匹配策略”,而没有想到“回退”的想法。思路上的回退也有问题,很容易陷到怪圈里去。

代码

#include <cstdio>

#define rep( i, a, b ) for( int i = (a) ; i <= (b) ; i ++ )
#define per( i, a, b ) for( int i = (a) ; i >= (b) ; i -- )

const int mod = 998244353;
const int MAXN = 305;

template<typename _T>
inline void Read( _T &x ) {
	x = 0; char s = getchar(); bool f = false;
	while( ! ( '0' <= s && s <= '9' ) ) { f = s == '-', s = getchar(); }
	while( '0' <= s && s <= '9' ) { x = ( x << 3 ) + ( x << 1 ) + ( s ^ '0' ), s = getchar(); }
	if( f ) x = -x;
}

template<typename _T>
inline void Write( _T x ) {
	if( x < 0 ) putchar( '-' ), x = -x;
	if( 9 < x ) Write( x / 10 );
	putchar( x % 10 + '0' );
}

int pref[MAXN], jmp[MAXN];

int f[2][MAXN][MAXN << 2];
char str[MAXN];

int N, T;

inline int Qkpow( int, int );
inline int Inv( const int &a ) { return Qkpow( a, mod - 2 ); }
inline int Mul( int x, const int &v ) { return 1ll * x * v % mod; }
inline int Sub( int x, const int &v ) { return ( x -= v ) < 0 ? x + mod : x; }
inline int Add( int x, const int &v ) { return ( x += v ) >= mod ? x - mod : x; }

inline int& MulEq( int &x, const int &v ) { return x = 1ll * x * v % mod; }
inline int& SubEq( int &x, const int &v ) { return ( x -= v ) < 0 ? ( x += mod ) : x; }
inline int& AddEq( int &x, const int &v ) { return ( x += v ) >= mod ? ( x -= mod ) : x; }

inline int Qkpow( int base, int indx ) {
	int ret = 1;
	while( indx ) {
		if( indx & 1 ) MulEq( ret, base );
		MulEq( base, base ), indx >>= 1;
	}
	return ret;
}

int main() {
	Read( N ), Read( T ), scanf( "%s", str + 1 );
	rep( i, 1, N ) 
		pref[i] = pref[i - 1] + ( str[i] == '0' ? +1 : -1 );
	rep( i, 0, N ) {
		jmp[i] = -1;
		per( k, i - 1, 0 )
			if( pref[k] < pref[i] ) {
				jmp[i] = k; break;
			}
	}
	int pre = 1, nxt = 0, M = N + 2 * T; 
	f[0][0][M] = 1;
	rep( i, 1, M ) {
		pre ^= 1, nxt ^= 1;
		rep( j, 0, N ) rep( k, - M, M ) {
			if( ! f[pre][j][k + M] ) continue;
			int cur = f[pre][j][k + M];
			// choose '0'
			if( j < N && str[j + 1] == '0' ) AddEq( f[nxt][j + 1][k + 1 + M], cur );
			else AddEq( f[nxt][j][k + 1 + M], cur );
			// choose '1'
			if( j < N && str[j + 1] == '1' ) AddEq( f[nxt][j + 1][k - 1 + M], cur );
			else if( k > pref[j] ) AddEq( f[nxt][j][k - 1 + M], cur );
			else if( ~ jmp[j] ) AddEq( f[nxt][jmp[j]][k - 1 + M], cur );
			f[pre][j][k + M] = 0;
		}
	}
	Write( f[nxt][N][pref[N] + M] ), putchar( '\n' );
	return 0;
}
posted @ 2022-08-10 12:13  crashed  阅读(105)  评论(0编辑  收藏  举报