• [ACNOI2022]不猜不行


    说老实话 c n b l o g s \rm cnblogs cnblogs 才像个写博客的地方。 C S D N \rm CSDN CSDN 是啥玩意儿啊 😦


    题目

    题目背景
    OUYE \textsf{OUYE} OUYE 一起学 O I \rm OI OI 是人生的豪赌。可怜的是,没有哪个赌场会让你赢多输少。

    题目描述
    你有 x    ( 0 ⩽ x < 1 ) x\;(0\leqslant x<1) x(0x<1) 钱,你可以赌博,金额为任意实数 y y y,但需要 { y i } \{y_i\} {yi} 单调不降。若 x ⩾ 1 x\geqslant 1 x1 你就赢,不可能实现了你就输。

    赌博规则:投 y y y 钱,有 p p p 概率得 2 y 2y 2y 钱,有 ( 1 − p ) (1{-}p) (1p) 概率血本无归。保证 p < 1 2 p<\frac{1}{2} p<21

    求最优策略下赢的概率。对 998244353 998244353 998244353 取模输出。

    数据范围与提示
    x = a 1 b 1 ,    p = a 2 b 2 x=\frac{a_1}{b_1},\;p=\frac{a_2}{b_2} x=b1a1,p=b2a2,有 a 1 < b 1 ⩽ 1 0 6 a_1<b_1\leqslant 10^6 a1<b1106 2 a 2 < b 2 ⩽ 1 0 6 2a_2<b_2\leqslant 10^6 2a2<b2106

    思路

    没有样例,我将一分不得。有了样例,得分仍然寥寥。数学题滚出 O I \rm OI OI 😭

    先解决 b 1 = 2 k b_1=2^k b1=2k,也就是 x x x 是二进制下有限小数。不猜结论是不可能的。

    结论:每次取 x x x 二进制下最后一个 1 1 1 对应的数去赌。

    证明:首先将条件放宽,不要求赌的数额不降。我们将证明,在这更宽松的决策树中,这是最优策略。

    下面是题解的证明。有些地方我不能很理解,存疑处将标出。

    c ( s ) c(s) c(s) s s s 的二进制 1 1 1 的个数, f ( x , n )    ( x ∈ N ) f(x,n)\;(x\in\Bbb N) f(x,n)(xN) x 2 n \frac{x}{2^n} 2nx 变成 1 1 1 的概率。我们先假设赌的数额只能是 2 − n 2^{-n} 2n 的倍数,因此这等价于 x → 2 n x\to 2^n x2n 每次赌整数金额。记 q = 1 − p q=1-p q=1p 则有递推式
    f ( x , n ) = p ⋅ f ( ⌈ x 2 ⌉ , n − 1 ) + q ⋅ f ( ⌊ x 2 ⌋ , n − 1 ) f(x,n)=p\cdot f\left(\left\lceil\frac{x}{2}\right\rceil,n{-}1\right)+q\cdot f\left(\left\lfloor{x\over 2}\right\rfloor,n{-}1\right) f(x,n)=pf(2x,n1)+qf(2x,n1)

    因为 2 ∤ x 2\nmid x 2x 时在赌, 2 ∣ x 2\mid x 2x 时在转移。

    不难发现,这其实是概率性地让 l o w b i t \rm lowbit lowbit 进位或消失。我们枚举在哪些 b i t \rm bit bit 上 “失手” 了,设为 i i i 。对于 lcp ( x , i ) \text{lcp}(x,i) lcp(x,i) 的部分,若均为 1 1 1,说明失手了,必须后面进位一口气飞跃;若均为 0 0 0,尽管没失手,为了最终进位,后面必须进位。对于 lcp \text{lcp} lcp 后一位,若 x x x 0 0 0 i i i 1 1 1 则无进位可能,反之则总可行。那么后面的 b i t \rm bit bit 的情况都不重要。不难发现这就是 i < x i<x i<x 的判断,由此
    f ( x , n ) = ∑ i = 0 x − 1 q c ( i ) p n − c ( i ) f(x,n)=\sum_{i=0}^{x-1}q^{c(i)}p^{n-c(i)} f(x,n)=i=0x1qc(i)pnc(i)

    上面的文字是一种理解方式;数学归纳法也可以证明之。

    接下来只需证明 ∀ k ∈ [ 0 , x ] ∩ Z \forall k\in[0,x]\cap\Bbb Z k[0,x]Z f ( x , n ) ⩾ p ⋅ f ( x + k , n ) + q ⋅ f ( x − k , n ) f(x,n)\geqslant p\cdot f(x{+k},n)+q\cdot f(x{-}k,n) f(x,n)pf(x+k,n)+qf(xk,n) 。按:此处应当对某物进行了归纳,类似最短路,只需验证距离标号不可被松弛。可是最短路可以按照“最短路边数”归纳,而你呢?
    p ( f ( x + k , n ) − f ( x , n ) ) ⩽ q ( f ( x , n ) − f ( x − k , n ) ) ⇔ ∑ i = 0 k − 1 q c ( i + x ) p n − c ( i + x ) + 1 ⩽ ∑ i = 1 k q c ( x − i ) + 1 p n − c ( x − i )

    p(f(x+k,n)f(x,n))q(f(x,n)f(xk,n))i=0k1qc(i+x)pnc(i+x)+1i=1kqc(xi)+1pnc(xi)
    p(f(x+k,n)f(x,n))i=0k1qc(i+x)pnc(i+x)+1q(f(x,n)f(xk,n))i=1kqc(xi)+1pnc(xi)

    我们证明一个超强不等关系:存在双射 f ( t ) : [ x , x + k ) ↦ [ x − k , x ) f(t):[x,x{+}k)\mapsto[x{-}k,x) f(t):[x,x+k)[xk,x) 使得
    q c ( i ) p n − c ( i ) + 1 ⩽ q c ( f ( i ) ) + 1 p n − c ( f ( i ) ) q^{c(i)}p^{n-c(i)+1}\leqslant q^{c(f(i))+1}p^{n-c(f(i))} qc(i)pnc(i)+1qc(f(i))+1pnc(f(i))

    即每一项都对应更小。高中数学题都不敢这么放。它等价于
    q c ( i ) − c ( f ( i ) ) − 1 ⩽ p c ( i ) − c ( f ( i ) ) − 1 ⇐ c ( i ) − c ( f ( i ) ) − 1 ⩽ 0 q^{c(i)-c(f(i))-1}\leqslant p^{c(i)-c(f(i))-1}\Leftarrow c(i)-c(f(i))-1\leqslant 0 qc(i)c(f(i))1pc(i)c(f(i))1c(i)c(f(i))10
    因为 q > p q>p q>p 嘛。最简单的思路是,令 f ( i ) f(i) f(i) i i i 去掉某个二进制位的结果。能否做到呢?

    s = 2 ⌈ log ⁡ 2 k ⌉ s=2^{\lceil\log_2 k\rceil} s=2log2k,对 i ∈ [ x − k + s ,    x + k ) i\in[x{-}k{+}s,\;x{+}k) i[xk+s,x+k) 规定 f ( i ) = i − s f(i)=i-s f(i)=is 。还剩下一个 [ x , x + s − k ) ↦ [ x + k − s , x ) [x,x{+}s{-}k)\mapsto[x{+}k{-}s,x) [x,x+sk)[x+ks,x) 需构造。不断递归即可完成构造。故结论成立。

    □ \square

    下面贴出另外两种“证明”,仅供参考。

    雨兔和沙鸭的证明

    如果不赌 l o w b i t \rm lowbit lowbit,赌更高 b i t \rm bit bit,那以后 l o w b i t \rm lowbit lowbit 就赌不得,进位不上来,就没用了。如果赌更低 b i t \rm bit bit,反正最后要么 l o w b i t \rm lowbit lowbit 进位要么不进位,不如赌。

    □ \square

    张教主的证明

    基本策略:若钱数 ⩽ 1 2 \leqslant\frac{1}{2} 21 all-in \text{all-in} all-in,否则赌 ( 1 − x ) (1{-}x) (1x) 。因为 p < 1 2 p<\frac{1}{2} p<21 所以期望下赌多了只会输钱,不如孤注一掷。我们承认它是最优的。称其为“基本策略”。

    然后我们证明它和另一策略等价:记 f ( x ) f(x) f(x) 为当前 x x x 钱时选择的赌注,则
    f ( x ) = { 1 2 ( x = 1 2 ) 1 4 − ∣ x − 1 4 ∣ ( x < 1 2 ) 1 4 − ∣ x − 3 4 ∣ ( x > 1 2 ) f(x)=

    {12(x=12)14|x14|(x<12)14|x34|(x>12)
    f(x)=2141x4141x43(x=21)(x<21)(x>21)

    如果将 f ( x ) f(x) f(x) 的图像画出来,那么原本是“山峰”的形状,而这一变化是将两个“山坡”再次换成“山峰”,只保留山巅的单点。

    证明过程是大力展开。这里就以 1 4 < x < 1 2 \frac{1}{4}<x<\frac{1}{2} 41<x<21 为例。
    g ( x ) = p ⋅ g ( 1 2 ) + q ⋅ g ( 2 x − 1 2 ) = p 2 + q p ⋅ g ( 4 x − 1 ) ( 0.25 < x < 0.5 )

    g(x)=pg(12)+qg(2x12)=p2+qpg(4x1)(0.25<x<0.5)
    g(x)=pg(21)+qg(2x21)=p2+qpg(4x1)(0.25<x<0.5)

    注意第二行是用“基本策略”展开, all-in \text{all-in} all-in 的结果。按:这里感觉像一个隐晦的归纳法,即“其他位置用这个策略拟合出了相同的胜率,因此可以直接用原策略的胜率方程展开”。但是这东西很难归纳。

    或者说,我证明了“调整第一步策略可以得到相同胜率”,而每一步都相当于第一步,所以每一步都可以调整?说起来总是怪怪的。

    而直接展开的结果是
    g ( x ) = p ⋅ g ( 2 x ) = p 2 + p q ⋅ g ( 4 x − 1 ) g(x)=p\cdot g(2x)=p^2+pq\cdot g(4x-1) g(x)=pg(2x)=p2+pqg(4x1)

    所以可行。同理,我们可以无限地将“山坡”替换成“山峰”。

    不难发现最终 f ( s 2 k ) = 1 2 k    ( 2 ∤ s ) f(\frac{s}{2^k})=\frac{1}{2^k}\;(2\nmid s) f(2ks)=2k1(2s) 。这就是每次赌 l o w b i t \rm lowbit lowbit 嘛。

    □ \square

    d p \tt dp dp 重写上面过程: f i , 0 / 1 f_{i,0/1} fi,0/1 表示当前是第 i i i 位,后是否有进位的赢概率。转移是线性变化。

    若当前 b i t \rm bit bit 0 0 0,则转移为
    f i = ( 1 0 q p ) f i − 1 f_i=

    (10qp)
    f_{i-1} fi=(1q0p)fi1

    其中 f i = ( f i , 0 f i , 1 ) f_i=

    (fi,0fi,1)
    fi=(fi,0fi,1) 。这个转移就不细说了,只需要足够的耐心。

    若当前 b i t \rm bit bit 1 1 1,则转移为
    f i = ( q p 0 1 ) f i − 1 f_i=

    (qp01)
    f_{i-1} fi=(q0p1)fi1

    对于 b i = 2 n b_i=2^n bi=2n 的情况,我们已经 O ( n ) \mathcal O(n) O(n) 解决了。否则 x x x 是无限循环小数。

    显然 x x x 的循环节长度不超过 b b b 。设单个循环节的矩阵是 B B B,非纯循环部分的矩阵是 A A A 。则只需求解
    lim ⁡ n → + ∞ B n A ( 0 1 ) \lim_{n\to+\infty}B^{n}A

    (01)
    n+limBnA(01)

    通用方法大概是对角化。但本题中 B B B 是非常返马尔科夫矩阵,可以直接解出稳态。注意是行向量!

    代码

    #include <cstdio>
    #include <algorithm> // Almighty XJX yyds!!
    #include <cstring> // oracle: ZXY yydBUS!!!
    #include <cctype> // Huge Egg Dog ate me!!!
    using llong = long long;
    # define rep(i,a,b) for(int i=(a); i<=(b); ++i)
    # define drep(i,a,b) for(int i=(a); i>=(b); --i)
    # define rep0(i,a,b) for(int i=(a); i!=(b); ++i)
    inline int readint(){
    	int a = 0, c = getchar(), f = 1;
    	for(; !isdigit(c); c=getchar()) if(c == '-') f = -f;
    	for(; isdigit(c); c=getchar()) a = a*10+(c^48);
    	return a*f;
    }
    
    const int MOD = 1e9+7;
    inline llong qkpow(llong b, int q){
    	llong a = 1;
    	for(; q; q>>=1,b=b*b%MOD) if(q&1) a = a*b%MOD;
    	return a;
    }
    
    struct Matrix{
    	int a00, a01, a10, a11;
    	Matrix operator * (const Matrix &b) const {
    		Matrix c;
    		c.a00 = int((llong(a00)*b.a00+llong(a01)*b.a10)%MOD);
    		c.a01 = int((llong(a00)*b.a01+llong(a01)*b.a11)%MOD);
    		c.a10 = int((llong(a10)*b.a00+llong(a11)*b.a10)%MOD);
    		c.a11 = int((llong(a10)*b.a01+llong(a11)*b.a11)%MOD);
    		return c;
    	}
    	static Matrix I(){
    		Matrix c; c.a00 = c.a11 = 1;
    		c.a01 = c.a10 = 0; return c;
    	}
    	Matrix bite(){
    		Matrix c;
    		if(a00 != 1){
    			// (a00-1)*x+a10*y = 0
    			// x+y = 1 => (a00-1-a10)*x+a10 = 0
    			c.a00 = c.a10 = int(qkpow(1+a10+MOD-a00,MOD-2)*a10%MOD);
    			c.a01 = c.a11 = 1+MOD-c.a00; // Markov
    		}
    		else{
    			// a01*x+(a11-1)*y = 0
    			// x+y = 1 => (a11-1-a01)*y+a01 = 0
    			c.a01 = c.a11 = int(qkpow(1+a01+MOD-a11,MOD-2)*a01%MOD);
    			c.a00 = c.a10 = 1+MOD-c.a11;
    		}
    		return c;
    	}
    };
    Matrix mat[2];
    
    const int MAXB = 1000000;
    int pos[MAXB]; bool bit[MAXB];
    int main(){
    	for(int T=readint(),a,b,p; T; --T){
    		a = readint(), b = readint(), p = readint();
    		p = int(qkpow(readint(),MOD-2)*p%MOD);
    		mat[0].a00 = 1, mat[0].a01 = 0;
    		mat[0].a10 = 1+MOD-p, mat[0].a11 = p;
    		mat[1].a00 = 1+MOD-p, mat[1].a01 = p;
    		mat[1].a10 = 0, mat[1].a11 = 1;
    		memset(pos, -1, b<<2);
    		int st = 0, ed = 0;
    		for(int i=0; true; ++i){
    			if(~pos[a]){ // cycle found
    				st = pos[a], ed = i; break;
    			}
    			pos[a] = i; // record
    			if((a<<=1) >= b)
    				a -= b, bit[i] = true;
    			else bit[i] = false;
    		}
    		Matrix ddg = Matrix::I();
    		rep0(i,st,ed) ddg = mat[bit[i]]*ddg;
    		ddg = ddg.bite(); // infinite power
    		drep(i,st-1,0) ddg = ddg*mat[bit[i]];
    		printf("%d\n",ddg.a01);
    	}
    	return 0;
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
  • 相关阅读:
    将把python项目打包成Docker镜像(linux版)
    2019史上最全java面试题题库大全800题含答案
    Python Interview Question Summary part 1
    git stash 用法
    JUnit单元测试
    【笔记】微信小程序右上角胶囊按钮(标题设置透明后的处理)
    入行测试一年半的心得体会
    vue 使用cnpm install 按钮node库 点击页面按钮没有任何反映
    Git-04-远程库
    【正版软件】Navicat Monitor 实时数据库监控工具,一套安全、简单而且无代理的远程服务器监控工具。
  • 原文地址:https://blog.csdn.net/qq_42101694/article/details/125417662