[清华集训 2017] 福若格斯

题目背景

小d是4xx9小游戏高手。

题目描述

有一天,小d发现了一个很经典的小游戏:跳青蛙。

游戏在一个5个格子的棋盘上进行。在游戏的一开始,最左边的两个格子上各有一个向右的青蛙,最右边的两个格子上各有一个向左的青蛙。

0

每次移动可以选取一个青蛙,向这只青蛙的前方移动一格到空格子中或跳过前方的一个不同朝向的青蛙并移动到空格子中。

1

2

为了使你更好地理解这个游戏,我们下发了一个游戏demo作为参考(注意:这个demo中的棋盘大小和题目中并不相同)。

这个游戏本身当然难不倒小d,小d轻松地就解决了这个游戏。但是一个人玩游戏实在是太寂寞了,于是小d找到了小m和他一起玩耍。小d规定,自己只能操控向右的青蛙,小m只能操控向左的青蛙。

小d很快发现,这个游戏想要做到双方轮流行动,就无法达到交换所有青蛙的游戏结局。于是,小d打开了m个游戏,并规定双方轮流行动,每次选择其中一个游戏并控制自己的青蛙行动一步(不能不动)。小d发现,这么做的话就能够使大部分的游戏最终都交换所有的青蛙了。

由于小d是坑队友高手,所以他们玩了一会之后,就开始互相坑害对方,都希望使对方无法行动。他们约定,当轮到一方行动时,若其所有的青蛙都无法行动,则对方获得游戏的胜利。正当博弈论大师小d计算着谁会成为最后的胜者时,电脑卡死了。小d发现,只能kill掉一些游戏才能使剩下的游戏进行下去了。由于电脑已经卡死了,小d无法自由选择kill掉哪些游戏,只能运行系统自带的随机kill小程序。具体来说,小d运行这个随机kill小程序之后,每个游戏有 1/2 ​​的概率被kill掉,有 1/2 ​​的概率能够继续下去。游戏之间被kill掉的概率是独立的。

小d思考了一番,决定如果运行小程序之后他的胜率过低,就直接重启电脑。这时,小d突然发现自己已经不记得刚才轮到谁行动了,于是他决定综合考虑自己先手和后手的胜率。

小d并不擅长概率论,他想让你告诉他运行小程序后,剩下的局面为小d必胜、小m必胜、先手必胜、后手必胜的概率各为多少,这样他才能更好地决定是否重启电脑。

为了避免精度问题,输出答案乘2�2m​​之后对998244353998244353取模的结果。

注意:题目并不保证输入的所有m个状态中小m和小d之前的总行动步数相差不超过1,但是保证不会出现起始状态无法到达的状态。

输入格式

从标准输入读入数据。

我们使用一个长度为5的字符串来表示一个状态,其中,L表示面朝右的青蛙,R表示面朝左的青蛙,_(下划线)表示空格子。例如,初始状态为LL_RR。

本题含有多组数据,第一行两个整数�,�T,C(1≤�≤1001≤C≤100)分别表示测试点编号和数据组数。

对于每组数据,第一行一个整数�n(1≤�≤231≤n≤23)表示不同状态的棋盘个数,接下来n行每行一个长度为5的字符串��si​​​和一个正整数��ai​​​(1≤��≤1061≤ai​≤106​​),分别表示棋盘的状态和在该状态下的棋盘的个数。

保证输入的字符串合法且不重复。

输出格式

输出到标准输出。

定义�=∑��m=∑ai​​​。

对于每组数据,输出一行四个整数,分别表示小d必胜(即L的控制方必胜)、小m必胜(即R的控制方必胜)、先手必胜、后手必胜的概率乘2�2m​​之后对998244353998244353取模的结果。

代码如下;

#include<bits/stdc++.h>
#define ll long long
#define re register
#define cs const

void file(){
#ifdef zxyoi
    freopen("frog.in","r",stdin);
#endif
    std::ios::sync_with_stdio(false);
}

using std::cin;
using std::cerr;
using std::cout;

cs int mod=998244353;
inline int add(int a,int b){return a+b>mod?a+b-mod:a+b;}
inline int dec(int a,int b){return a-b<0?a-b+mod:a-b;}
inline int mul(int a,int b){ll r=(ll)a*b;return r>=mod?r%mod:r;}
inline int po(int a,int b){
    int r=1;for(;b;b>>=1,a=mul(a,a))
    if(b&1)r=mul(r,a);return r;
}
inline void Inc(int &a,int b){a+=b-mod;a+=a>>31&mod;}
inline void Dec(int &a,int b){a-=b;a+=a>>31&mod;}
inline void Mul(int &a,int b){a=mul(a,b);}

cs int N=1e6+7;
int fac[N],ifc[N];
void init_fac(){
    fac[0]=1;for(int re i=1;i<N;++i)fac[i]=mul(fac[i-1],i);
    ifc[N-1]=po(fac[N-1],mod-2);
    for(int re i=N-1;i;--i)ifc[i-1]=mul(ifc[i],i);
}
inline int C(int n,int m){
    return mul(fac[n],mul(ifc[m],ifc[n-m]));
}

void init_C(int n,int *a){
    for(int re i=0;i<=n;++i)a[i]=C(n,i);
}
int a[N],b[N],c[N];
// 0   1  2  3  4  5  6  7
//-1 -1/2 0 1/2 1  * up dn
int ct[11];
//<0, =0 ,>0
int num[15],ans[15],cnt[15];
int sign(int x){
    switch(x){
        case 1:return 3;
        case 0:return 2;
        case -1:return 1;
        default:return x<0?0:4;
    }
}
void solve(){
    int n,m=0;cin>>n;
    memset(ct,0,sizeof ct);
    memset(num,0,sizeof num);
    memset(ans,0,sizeof ans);
    memset(cnt,0,sizeof cnt);
    while(n--){
        std::string s;int x;
        cin>>s>>x;m+=x;
        if(s=="LL_RR")ct[5]+=x;
        if(s=="L_LRR")ct[6]+=x;
        if(s=="_LLRR")ct[2]+=x;
        if(s=="LRL_R")ct[5]+=x;
        if(s=="LR_LR")ct[2]+=x;
        if(s=="_RLLR")ct[0]+=x;
        if(s=="R_LLR")ct[2]+=x;
        if(s=="LRRL_")ct[4]+=x;
        if(s=="LRR_L")ct[2]+=x;
        if(s=="LRLR_")ct[2]+=x;
        if(s=="LR_RL")ct[1]+=x;
        if(s=="_RLRL")ct[0]+=x;
        if(s=="R_LRL")ct[2]+=x;
        if(s=="RRL_L")ct[4]+=x;
        if(s=="RR_LL")ct[2]+=x;
        if(s=="LLR_R")ct[7]+=x;
        if(s=="L_RLR")ct[5]+=x;
        if(s=="_LRLR")ct[2]+=x;
        if(s=="RL_LR")ct[3]+=x;
        if(s=="RLRL_")ct[4]+=x;
        if(s=="RLR_L")ct[2]+=x;
        if(s=="R_RLL")ct[0]+=x;
        if(s=="LLRR_")ct[2]+=x;
    }
    init_C(ct[0]+ct[4],a);//-1 ,1
    init_C(ct[1]+ct[3],b);//-1/2 ,1/2
    init_C(ct[7]+ct[6],c);//up ,dn
    for(int re i=1;i<=ct[1]+ct[3];++i)Inc(b[i],b[i-1]);
    for(int re i=0,j=ct[1]+ct[3],k=j;i<=ct[0]+ct[4];++i){
        while(~j&&(i-ct[0])*2+(j-ct[1])>=0)--j;
        while(~k&&(i-ct[0])*2+(k-ct[1])>0)--k;
        if(~j)Inc(num[0],mul(a[i],b[j]));
        if(~k)Inc(num[1],mul(a[i],b[k]));
    }
    num[2]=po(2,ct[0]+ct[1]+ct[2]+ct[3]+ct[4]);
    Mul(num[0],po(2,ct[2]));
    Mul(num[1],po(2,ct[2]));
    Dec(num[2],num[1]);
    Dec(num[1],num[0]);    
    for(int re i=0;i<=ct[6]+ct[7];++i)
    Inc(cnt[sign(i-ct[7])],c[i]);    
    ans[0]=mul(num[2],po(2,ct[5]+ct[6]+ct[7]));
    ans[1]=mul(num[0],po(2,ct[5]+ct[6]+ct[7]));
    if(ct[5]==0){
        Inc(ans[0],mul(num[1],cnt[3]+cnt[4]));
        Inc(ans[1],mul(num[1],cnt[0]+cnt[1]));
        Inc(ans[3],mul(num[1],cnt[2]));
    }else {
        int p=mul(po(2,ct[5]-1),num[1]);
        Inc(ans[0],mul(p,cnt[3]+cnt[4]));
        Inc(ans[1],mul(p,cnt[0]+cnt[1]));
        Inc(ans[3],mul(p,cnt[2]));
        Inc(ans[0],mul(p,cnt[4]));
        Inc(ans[1],mul(p,cnt[0]));
        Inc(ans[2],mul(p,add(cnt[1],add(cnt[2],cnt[3]))));
    }
    cout<<ans[0]<<" "<<ans[1]<<" "<<ans[2]<<" "<<ans[3]<<"\n";
}

signed main(){file();init_fac();int T,id;cin>>id>>T;while(T--)solve();return 0;}
 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值