• LOJ2327 「清华集训 2017」福若格斯 【不平等博弈】


    题目链接:LOJ

    对于这道题,我们要分3步来做它。

    1. 什么是 Surreal Number 及如何解决博弈问题。
    2. 如何用 Surreal Number 解决这道题。
    3. 推出结论之后如何计数

    首先看看这篇文章(如果真要看那本书的话会耗死你)

    然后我们来总结总结。

    首先,博弈局面可以对应到超现实数,它的左集是( ext{L})走一步的局面,右集是( ext{R})走一步的局面,用( ext{L})是否“赢得更爽”来判定大小,显然左集的数都小于它,它也小于右集的数(因为多走一步就是更接近无法行动)

    那游戏的相反数是什么呢?显然就是将( ext{L})( ext{R})玩家调换,然后就可以了。

    还有游戏的和。我们知道游戏的和是两个游戏可以选一个走,那么

    [x+y={(x_L+y)cup(x+y_L)|(x_R+y)cup(x+y_R)} ]

    我们发现它满足普通(+)的一堆运算律。所以它就是超现实数的加法了。

    至于乘法,这东西对博弈游戏没有用,不管了。

    然后我们发现,正数表示( ext{L})必胜,负数表示(R)必胜,(0)表示后手必胜。现在已经推出了一个结论,于是你开始手玩这道题的23个局面。

    但是你发现还有一种新东西叫做(*={0|0}),它是一个先手必胜的状态,并且还有两个东西叫做(uparrow={0|*})(downarrow={*|0})。用这些东西推推可以得到一些结论。

    1. (*,uparrow,downarrow)加上一个正数都是( ext{L})必胜,加上一个负数都是( ext{R})必胜。换句话说,非0数对这三个东西是绝对的(自己造了个词)
    2. (*+*=0),所以偶数个(*)会互相抵消。
    3. (uparrow>0,downarrow<0,uparrow+downarrow=0),所以(uparrow)(downarrow)可以互相抵消。但是要注意(*={downarrow|uparrow})
    4. (*+uparrow+uparrow>0,*+downarrow+downarrow<0),而(*+uparrow,*+downarrow)都是先手赢的局面。也就是说,(2uparrow>*>uparrow>0),而(downarrow)同理(注意(*​)是没有符号的)

    于是你根据这些东西,就可以讨论出以下的结论(以下将(downarrow)视作(-uparrow)):

    1. (*)个数为偶数

      1. (sum 数字>0or(sum 数字=0and sumuparrow>0)),则( ext{L})胜。

      2. (sum 数字=0and sumuparrow=0​),则后手胜。

      3. (sum 数字<0or(sum 数字=0andsumuparrow<0)),则( ext{R})胜。

    2. (*)个数为奇数

      1. (sum 数字>0and(sum 数字=0andsumuparrow>1)),则( ext{L})胜。
      2. (sum 数字=0and |sumuparrow|le 1),则先手胜
      3. (sum 数字<0or (sum 数字=0andsumuparrow<-1)),则( ext{R})胜。

    还有这个游戏的局面

    然后你发现,它是求(2^m)种情况中四种情况的和。所以还要考虑如何计数。

    首先( ext{num},*,uparrow)是相互独立的,所以你可以求出有多少种情况满足(sum ext{num})大于(0),小于(0)和等于(0),求出满足(sum uparrow)大于(1),小于(-1)([-1,1])的情况。

    上面两个东西是同理的,所以只说前一个,设(a,b,c,d)表示(-1,-frac{1}{2},frac{1}{2},1)的数量,那么就是求

    [(1+x^{-2})^a(1+x^{-1})^b(1+x)^c(1+x^2)^d=frac{(1+x)^{b+c}(1+x^2)^{a+d}}{x^{2a+b}} ]

    的次数(>0)(<0)(=0)对应的系数。首先把((1+x)^{b+c})按照二项式定理展开,然后枚举((1+x^2)^{b+d})的每一项,你发现(>0)(<0)对应的是((1+x)^{b+c})的两段。用前缀和搞搞就可以了。

    最后合并就是用乘法原理搞搞就可以了。

    具体可以看这个4KB多的代码。

    #include<bits/stdc++.h>
    #define Rint register int
    using namespace std;
    typedef long long LL;
    const int N = 2000003, mod = 998244353;
    int Id, T, n, cnt[8], fac[N], inv[N], x[N];
    inline void upd(int &a, int b){a += b; if(a >= mod) a -= mod;}
    inline int sub(int a, int b){return (a < b) ? (a - b + mod) : (a - b);}
    inline int add(int a, int b){return (a + b >= mod) ? (a + b - mod) : (a + b);}
    inline int kasumi(int a, int b){
    	int res = 1;
    	while(b){
    		if(b & 1) res = (LL) res * a % mod;
    		a = (LL) a * a % mod; b >>= 1;
    	}
    	return res;
    }
    inline void init(int m){
    	fac[0] = 1;
    	for(Rint i = 1;i <= m;i ++) fac[i] = (LL) fac[i - 1] * i % mod;
    	inv[m] = kasumi(fac[m], mod - 2);
    	for(Rint i = m;i;i --) inv[i - 1] = (LL) inv[i] * i % mod;
    }
    inline int C(int n, int m){
    	if(n < 0 || m < 0 || n < m) return 0;
    	return (LL) fac[n] * inv[m] % mod * inv[n - m] % mod;
    }
    /*0-4 : -1~1,5:*, 6:up, 7:down */
    int main(){
    #ifdef NTFOrzs
    	freopen("a.in", "r", stdin);
    #endif
    	scanf("%d%d", &Id, &T); init(2e6);
    	while(T --){
    		memset(cnt, 0, sizeof cnt);
    		scanf("%d", &n);
    		for(Rint i = 1;i <= n;i ++){
    			char opt[10]; int x;
    			scanf("%s%d", opt, &x);
    			if (!strcmp(opt, "LL_RR")) cnt[5] += x;
    			else if (!strcmp(opt, "L_LRR")) cnt[6] += x;
    			else if (!strcmp(opt, "LLR_R")) cnt[7] += x;
    			else if (!strcmp(opt, "LRL_R")) cnt[5] += x;
    			else if (!strcmp(opt, "L_RLR")) cnt[5] += x;
    			else if (!strcmp(opt, "_RLLR")) cnt[0] += x;
    			else if (!strcmp(opt, "LRRL_")) cnt[4] += x;
    			else if (!strcmp(opt, "LR_RL")) cnt[1] += x;
    			else if (!strcmp(opt, "RL_LR")) cnt[3] += x;
    			else if (!strcmp(opt, "_RLRL")) cnt[0] += x;
    			else if (!strcmp(opt, "RLRL_")) cnt[4] += x;
    			else if (!strcmp(opt, "RRL_L")) cnt[4] += x;
    			else if (!strcmp(opt, "R_RLL")) cnt[0] += x;
    			else cnt[2] += x;
    		}
    #ifdef NTFOrzs
    		printf("cnt[0] = %d, cnt[1] = %d, cnt[2] = %d, cnt[3] = %d, cnt[4] = %d, cnt[5] = %d, cnt[6] = %d, cnt[7] = %d
    ", cnt[0], cnt[1], cnt[2], cnt[3], cnt[4], cnt[5], cnt[6], cnt[7]);
    #endif
    		for(Rint i = 0;i <= cnt[1] + cnt[3];i ++) x[i] = C(cnt[1] + cnt[3], i);
    		for(Rint i = 1;i <= cnt[1] + cnt[3];i ++) upd(x[i], x[i - 1]);
    		int res1 = 0, res2 = 0, res3 = 0;
    		for(Rint i = 0;i <= cnt[0] + cnt[4];i ++){
    			int tmp = C(cnt[0] + cnt[4], i), t2 = 2 * (cnt[0] - i) + cnt[1];
    			upd(res2, (LL) tmp * C(cnt[1] + cnt[3], t2) % mod);
    			if(t2 >= 1) upd(res1, (LL) tmp * x[min(t2 - 1, cnt[1] + cnt[3])] % mod);
    			if(t2 >= 0 && t2 <= cnt[1] + cnt[3]) upd(res3, (LL) tmp * sub(x[cnt[1] + cnt[3]], x[t2]) % mod);
    			else if(t2 < 0) upd(res3, (LL) tmp * x[cnt[1] + cnt[3]] % mod);
    		}
    #ifdef NTFOrzs
    		printf("%d %d %d
    ", res1, res2, res3);
    #endif
    		int ans0 = 0, ans1 = 0, ans2 = 0, ans3 = 0, tmp1 = 0, tmp2 = 0, tmp3 = 0, tmp4 = 0, tmp5 = 0;
    		for(Rint i = 0;i <= cnt[7];i ++) x[i] = C(cnt[7], i);
    		for(Rint i = 1;i <= cnt[7];i ++) upd(x[i], x[i - 1]);
    		for(Rint i = 0;i <= cnt[6];i ++){
    			int tmp = C(cnt[6], i);
    			upd(tmp3, (LL) tmp * C(cnt[7], i) % mod);
    			upd(tmp2, (LL) tmp * C(cnt[7], i + 1) % mod);
    			upd(tmp4, (LL) tmp * C(cnt[7], i - 1) % mod);
    			if(i >= 2) upd(tmp5, (LL) tmp * x[min(i - 2, cnt[7])] % mod);
    			if(i <= cnt[7] - 2) upd(tmp1, (LL) tmp * sub(x[cnt[7]], x[i + 1]) % mod);
    		}
    #ifdef NTFOrzs
    		printf("%d %d %d %d %d
    ", tmp1, tmp2, tmp3, tmp4, tmp5);
    #endif
    		upd(ans0, add(tmp4, tmp5)); upd(ans1, add(tmp1, tmp2)); upd(ans3, tmp3);
    		if(cnt[5] > 0){
    			upd(ans0, tmp5); upd(ans1, tmp1); upd(ans2, add(tmp2, add(tmp3, tmp4)));
    #ifdef NTFOrzs
    			printf("%d %d %d %d
    ", ans0, ans1, ans2, ans3);
    #endif
    			int tmp = kasumi(2, cnt[5] - 1);
    			ans0 = (LL) ans0 * tmp % mod;
    			ans1 = (LL) ans1 * tmp % mod;
    			ans2 = (LL) ans2 * tmp % mod;
    			ans3 = (LL) ans3 * tmp % mod;
    		}
    		ans0 = (LL) ans0 * res2 % mod * kasumi(2, cnt[2]) % mod;
    		ans1 = (LL) ans1 * res2 % mod * kasumi(2, cnt[2]) % mod;
    		ans2 = (LL) ans2 * res2 % mod * kasumi(2, cnt[2]) % mod;
    		ans3 = (LL) ans3 * res2 % mod * kasumi(2, cnt[2]) % mod;
    		upd(ans0, (LL) res3 * kasumi(2, cnt[2] + cnt[5] + cnt[6] + cnt[7]) % mod);
    		upd(ans1, (LL) res1 * kasumi(2, cnt[2] + cnt[5] + cnt[6] + cnt[7]) % mod);
    		printf("%d %d %d %d
    ", ans0, ans1, ans2, ans3);
    	}
    }
    
  • 相关阅读:
    spring的好处
    2.3 java中类路径
    java的编译器为什么会自动编译java文件
    oracle添加字段或者删除字段-转载
    sql 取新的列名含义
    window.onload =writeMessage(); 与window.onload =writeMessage;的区别
    HTML DOM 之<textare>标签
    最新学习网址大全
    C#读写txt文件的两种方法介绍
    固定分隔符字符串与数组互转及ArrayList与数组(Array)互转
  • 原文地址:https://www.cnblogs.com/AThousandMoons/p/11748196.html
Copyright © 2020-2023  润新知