[硫化铂]开心消消乐

开心消消乐

题目概述

在这里插入图片描述
在这里插入图片描述

题解

首先很容易联想到 Gym 102586J 于是我们自然而然可以想到通过建立一个dfa来处理。
但显然,我们这个dfa怎么建才是关键。

我们观察到我们每次是将三个连续的字符脱水缩合成一个字符,事实上我们每次可以将前面的两个字符当成一个函数,而最后一位当作传入函数的参数。
实际上, 我们发现,我们的每次操作相当于以最后一个数作为参数,将前面的函数不断嵌套。
而我们的函数总共只有四种:不变,交换位置,全赋 0 / 1 0/1 0/1,它们之间的嵌套是具有封闭性的,即无论怎么嵌套都是这四种函数之一。
由于我们每次的操作都是针对一个前缀的,我们不妨记录下我们这些前缀能够构造出上面的哪些函数,记 d p i , j dp_{i,j} dpi,j表示我们使用长度为 i i i的前缀,可以构造出的函数集合为 j j j
显然,当加入的两个字符确定时,我们函数集合可以有两种变化,一是用 h ( x ) = f ( g ( x ) ) h(x)=f(g(x)) h(x)=f(g(x))的方式,与前面的函数嵌套起来,一是将第一个字符传到前面的函数中,用返回值和后一个字符得到新的函数。
这两种方式得到函数集合的并,就是我们转移到的新函数集合。
我们可以通过这种方式,确定我们转移到的集合。但由于加入的两个字符不是确定的,所以我们还得去枚举新加入的字符。
我们不妨将我们的函数集合就看做dfa上的点,而加入的字符就看做dfa上的边,那我们的整个过程就是在dfa上跑dp。
显然,我们可以先将这个dfa的图预处理出来,然后在上面跑就行了。

时间复杂度 O ( T n ) O\left(Tn\right) O(Tn),不过还有个 2 5 2^5 25的常数。

源码

#include<bits/stdc++.h>
using namespace std;
#define MAXM (1<<4)+5
#define MAXN 100005
#define lowbit(x) (x&-x)
#define reg register
#define pb push_back
#define mkpr make_pair
#define fir first
#define sec second
#define lson (rt<<1)
#define rson (rt<<1|1)
typedef long long LL;
typedef unsigned long long uLL; 
typedef long double ld;
typedef pair<int,int> pii;
const int INF=0x3f3f3f3f;
const int mo=998244353;
const int mod=1e5+3;
const int inv2=499122177;
const int jzm=2333;
const int zero=2000;
const int n1=2000;
const int M=100000;
const int orG=3,ivG=332748118;
const long double Pi=acos(-1.0);
const double eps=1e-12;
template<typename _T>
_T Fabs(_T x){return x<0?-x:x;}
template<typename _T>
void read(_T &x){
	_T f=1;x=0;char s=getchar();
	while(s>'9'||s<'0'){if(s=='-')f=-1;s=getchar();}
	while('0'<=s&&s<='9'){x=(x<<3)+(x<<1)+(s^48);s=getchar();}
	x*=f;
}
template<typename _T>
void print(_T x){if(x<0){x=(~x)+1;putchar('-');}if(x>9)print(x/10);putchar(x%10+'0');}
int gcd(int a,int b){return !b?a:gcd(b,a%b);}
int add(int x,int y,int p){return x+y<p?x+y:x+y-p;}
void Add(int &x,int y,int p){x=add(x,y,p);}
int qkpow(int a,int s,int p){int t=1;while(s){if(s&1)t=1ll*t*a%p;a=1ll*a*a%p;s>>=1;}return t;}
int n,T,a[10],typ[5],b[MAXN],dp[2][MAXM],tur[5][5],nxt[20][5],ans;
char str[MAXN];
int match(int x,int y){return (x^y)?x:2+x;}
int ask(int x,int y){return x>1?x-2:(x^y);}
signed main(){
	freopen("game.in","r",stdin);
	freopen("game.out","w",stdout);
	read(T);
	tur[0][0]=0;tur[0][1]=1;tur[0][2]=2;tur[0][3]=3;
	tur[1][0]=1;tur[1][1]=0;tur[1][2]=3;tur[1][3]=2;
	tur[2][0]=tur[2][1]=tur[2][2]=tur[2][3]=2;
	tur[3][0]=tur[3][1]=tur[3][2]=tur[3][3]=3;
	while(T--){
		for(int i=0;i<8;i++)scanf("%1d",&a[i]);
		for(int i=0;i<4;i++)typ[i]=match(a[i],a[i+4]);
		scanf("\n%s",str+1);n=(int)strlen(str+1);
		for(int i=1;i<=n;i++)b[i]=(str[i]=='?'?2:((str[i]-'0')^1)),str[i]=0;
		for(int i=0;i<2;i++)for(int j=0;j<16;j++)dp[i][j]=0;
		int now=0,las=1;dp[now][1]=1;
		for(int i=0;i<16;i++)
			for(int k1=0;k1<2;k1++)
				for(int k2=0;k2<2;k2++){
					int S=k1|(k2<<1);nxt[i][S]=0;
					for(int j=0;j<4;j++)if((i>>j)&1)nxt[i][S]|=(1<<tur[j][typ[S]]);
					for(int j=0;j<4;j++)if((i>>j)&1)nxt[i][S]|=(1<<typ[ask(j,k1)|(k2<<1)]);
				}
		for(int i=1;i<n;i+=2){
			swap(now,las);
			for(int j=0;j<16;j++)dp[now][j]=0;
			for(int k1=0;k1<2;k1++)if(b[i]^k1)
				for(int k2=0;k2<2;k2++)if(b[i+1]^k2)
					for(int j=0;j<16;j++)if(dp[las][j])
						Add(dp[now][nxt[j][k1|(k2<<1)]],dp[las][j],mo);
		}
		for(int i=0;i<2;i++)if(b[n]^i)
			for(int j=0;j<16;j++)if(dp[now][j]){
				bool flag=0;
				for(int k=0;k<4;k++)if((j>>k)&1)
					flag|=ask(k,i);
				if(flag)Add(ans,dp[now][j],mo);
			}
		printf("%d\n",ans);ans=0;
	}
	return 0;
}

谢谢!!!

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值