题解 开心消消乐

传送门

神仙题

首先令 \(f_{i, s}\) 表示到点 \(i(2\mid i)\) 的状态为 \(s\) 可以 \(n^2\) 求出合法的消除操作序列数
一个优化:令 \(G[a, b](x)\) 为当前栈中情况,当 x=0 时返回 a,x=1 时返回 b
取或可以 \(O(n)\) 完成没有 ? 的判定

于是考虑带上 ‘?’
image
我 tm 谢谢你
题解写得 很 简 洁

  • DP 套 DP 貌似还可以用来处理合法状态数一算就重的情况
    比如有并非对于数位限制的数位 DP 之类

于是考虑令 \(f_{i, a, b, c, d}\) 为当前状态是否满足加入 0/1 能得到 0/1
转移考虑预处理一个 \(tr_{s, t}\) 表示 \(f_{i, s}\) 与 状态 \(t\) 能转移到哪个状态
于是转移就很简单了
那看这个东西怎么预处理
再预处理出 \(buc_{s, t}\) 表示 \(G[a, b](x)\)\(t\) 能转移到哪些 \(G[a', b'](x)\)
于是从 a/b, c/d 中枚举全 1 对进行转移即可
状态数是 \(O(2^{2^2}n)\)

点击查看代码
#include <bits/stdc++.h>
using namespace std;
#define INF 0x3f3f3f3f
#define N 100010
#define pb push_back
#define ll long long
//#define int long long

int n;
char c[N];
int val[N];
const ll mod=998244353;
inline void md(ll& a, ll b) {a+=b; a=a>=mod?a-mod:a;}

#if 0
namespace task1{
	bool g[N][2];
	void solve() {
		memset(g, 0, sizeof(g));
		for (int i=1; i<=n; ++i) s[i]-='0';
		g[2][0]=val[(0<<2)|(s[2]<<1)|(s[1])];
		g[2][1]=val[(1<<2)|(s[2]<<1)|(s[1])];
		for (int i=4; i<=n; i+=2) {
			int t=g[i-2][s[i-1]];
			g[i][0]|=val[(0<<2)|(s[i]<<1)|t];
			g[i][1]|=val[(1<<2)|(s[i]<<1)|t];
			g[i][0]|=g[i-2][val[(0<<2)|(s[i]<<1)|(s[i-1])]];
			g[i][1]|=g[i-2][val[(1<<2)|(s[i]<<1)|(s[i-1])]];
		}
		//for (int i=2; i<=n; i+=2) printf("g[%d][0]=%d, g[%d][1]=%d\n", i, g[i][0], i, g[i][1]);
		cout<<g[n-1][s[n]]<<endl;
	}
}
#endif

namespace task{
	int tr[1<<4][1<<2];
	ll f[N][1<<4], ans;
	vector<int> buc[1<<2][1<<2];
	void init() {
		for (int s=0; s<4; ++s) {
			for (int t=0; t<4; ++t) {
				int k=(t&1)?(s>>1):(s&1);
				//cout<<"k: "<<k<<endl;
				buc[s][t].pb(val[(0<<2)|(t&2)|k]|(val[(1<<2)|(t&2)|k]<<1));
				buc[s][t].pb( ((s&(1<<val[(0<<2)|t]))?1:0) | (((s&(1<<val[(1<<2)|t]))?1:0)<<1) );
				sort(buc[s][t].begin(), buc[s][t].end());
				buc[s][t].erase(unique(buc[s][t].begin(), buc[s][t].end()), buc[s][t].end());
			}
		}
		for (int s=0; s<16; ++s) {
			for (int t=0; t<4; ++t) {
				for (int i=0; i<2; ++i) if (s&(1<<i)) {
					for (int j=0; j<2; ++j) if (s&(4<<j)) {
						int g=i|(j<<1);
						for (auto it:buc[g][t]) {
							//tr[s][t]|=1<<it, assert(it<4);
							int k=0;
							k |= ( (it&1) ? 2 : 1);
							k |= ( (it&2) ? 8 : 4);
							tr[s][t]|=k;
						}
					}
				}
			}
		}
	}
	void solve() {
		ans=0;
		memset(tr, 0, sizeof(tr));
		memset(f, 0, sizeof(f));
		for (int s=0; s<4; ++s) for (int t=0; t<4; ++t) buc[s][t].clear();
		init();
		//for (int s=0; s<4; ++s) for (int t=0; t<4; ++t) {cout<<"buc of "<<bitset<2>(s)<<' '<<bitset<2>(t)<<": "; for (auto it:buc[s][t]) cout<<bitset<2>(it)<<' '; cout<<endl;}
		//for (int s=0; s<16; ++s) for (int t=0; t<4; ++t) {cout<<"tr: "<<bitset<4>(s)<<' '<<bitset<2>(t)<<' '<<bitset<4>(tr[s][t])<<endl;}
		for (int t=0; t<4; ++t) {
			if ( (t&1 && c[1]=='0') || (!(t&1) && c[1]=='1') ) continue;
			if ( (t&2 && c[2]=='0') || (!(t&2) && c[2]=='1') ) continue;
			int s=0;
			s |= ( (val[(0<<2)|t]) ? 2 : 1);
			s |= ( (val[(1<<2)|t]) ? 8 : 4);
			++f[2][s];
		}
		for (int i=4; i<=n; i+=2) {
			//cout<<"i: "<<i<<endl;
			for (int s=0; s<16; ++s) {
				for (int t=0; t<4; ++t) {
					if ( (t&1 && c[i-1]=='0') || (!(t&1) && c[i-1]=='1') ) continue;
					if ( (t&2 && c[i]=='0') || (!(t&2) && c[i]=='1') ) continue;
					//cout<<"st: "<<bitset<4>(s)<<' '<<bitset<2>(t)<<' '<<bitset<4>(tr[s][t])<<' '<<f[i-2][s]<<endl;
					md(f[i][tr[s][t]], f[i-2][s]);
					//cout<<f[i][tr[s][t]]<<endl;
				}
			}
		}
		for (int t=0; t<2; ++t) {
			if (c[n]!='?' && c[n]-'0'!=t) continue;
			for (int s=0; s<16; ++s) if (s&(1<<(t<<1|1))) 
				md(ans, f[n-1][s]);
		}
		#if 0
		cout<<"f: "<<endl;
		for (int i=2; i<=n; i+=2) {
			for (int s=0; s<16; ++s) cout<<"f["<<i<<"]["<<bitset<4>(s)<<"]="<<f[i][s]<<endl;
		}
		#endif
		cout<<ans<<endl;
	}
}

signed main()
{
	freopen("game.in", "r", stdin);
	freopen("game.out", "w", stdout);

	int T;
	scanf("%d", &T);
	while (T--) {
		scanf("%s", c);
		for (int i=0; i<8; ++i) val[i]=c[i]^48;
		scanf("%s", c+1);
		n=strlen(c+1);
		task::solve();
	}
	
	return 0;
}
posted @ 2022-02-15 10:47  Administrator-09  阅读(4)  评论(2编辑  收藏  举报