本文主要是介绍[硫化铂]开心消消乐,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
开心消消乐
题目概述
题解
首先很容易联想到 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;
}
谢谢!!!
这篇关于[硫化铂]开心消消乐的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!