zoukankan      html  css  js  c++  java
  • @loj


    @description@

    小 S 在和小 F 玩一个叫「斗地主」的游戏。

    可怜的小 S 发现自己打牌并打不过小 F,所以他想要在洗牌环节动动手脚。

    一副牌一共有 n 张牌,从上到下依次标号为 1~n 。标号为 (i) 的牌分数(f(i))。在本题,(f(i)) 有且仅有两种可能:(f(i) = i)(f(i) = i^2)

    洗牌的方式和我们日常生活中的比较类似,以下我们用形式化的语言来定义:

    洗牌环节一共分 m 轮,这 m 轮洗牌依次进行。第 i 轮洗牌时:

    1. 小 S 会拿出从最上面往下数的前 (A_i) 张牌。这样这副牌就被分成了两堆:第一堆是最上面的 (A_i) 张牌,第二堆是剩下的 (n - A_i) 张牌,且这两堆牌内相对顺序不变。特别地,当 (A_i = n)(A_i = 0) 时,有一堆牌是空的。
    2. 接下来对两堆牌进行合并,从而产生新的第三堆牌。当第一堆牌还剩下 (X) 张,第二堆牌还剩下 (Y) 张的时候,以 (frac{X}{X + Y}) 的概率取出第一堆牌的最下面的牌,并将它放入新的第三堆牌的最上面,(frac{Y}{X + Y}) 的概率取出第二堆牌的最下面的牌,并将它放入新的第三堆牌的最上面。
    3. 重复操作 2,一直取到两堆牌都为空为止。这样我们就完成了一轮洗牌。

    因为洗牌过程是随机的,所以小 S 发现自己没法知道某个位置上具体是哪张牌。但小 S 想问你在经历了这 m 轮洗牌后,某个位置上的牌的期望分数是多少。小 S 一共会问你 Q 个这样的问题。

    原题传送门。

    @solution@

    分析一下,洗牌操作就是不改变两堆牌的内部顺序,随机从 ({nchoose A}) 种合法方案中选择一种。

    先看 (type = 1)。经过打表差分 + 目瞪口呆观察法可得,无论洗牌多少次,期望值与位置呈一次函数关系。

    再看 (type = 2)。虽然目瞪口呆看不出来,但是联系 (type=1) 不难猜测期望值与位置呈二次函数关系。经过二次差分 + 再次目瞪口呆观察法可以验证的确如此。

    然后就随便维护 3 张牌,拉格朗日插值即可。这道题就从noi难度变成了noip难度。


    证明?虽然你不证明也能过,不过我们还是来理性愉悦一下吧。

    不妨假设 (n < m)(m < 0) 时都有 ({nchoose m} = 0),写式子:

    [g_x = frac{1}{nchoose A}[sum_{i=1}^{A}{x-1choose i-1}{n-xchoose A-i}f_i + sum_{i=1}^{n-A}{x-1choose i-1}{n-xchoose n-A-i}f_{i+A}] ]

    我们取 (sum_{i=1}^{A}{x-1choose i-1}{n-xchoose A-i}) 部分继续化简:

    [egin{aligned} sum_{i=1}^{A}{x-1choose i-1}{n-xchoose A-i} &= sum_{i=1}^{A}frac{(x-1)!(n-x)!}{(i-1)!(A-i)!(x-i)!(n-x-A+i)!} \ &= frac{(x-1)!(n-x)!}{(A-1)!(n-A)!} imessum_{i=1}^{A}{A-1choose i-1}{n-Achoose x-i} \ &= frac{(x-1)!(n-x)!}{(A-1)!(n-A)!} imes{n-1choose x-1} \ &= {n - 1choose A - 1} end{aligned} ]

    当然你可以从组合意义更快地得到该式子,展示代数过程是为了方便下面的推广:

    类似地,你也可以证明:

    [egin{aligned} sum_{i=1}^{A}{x-1choose i-1}{n-xchoose A-i}(i-1)^{underline{k}} &={n - k - 1choose A - k - 1}(x-1)^{underline{k}} end{aligned} ]

    对于后面 (sum_{i=1}^{n-A}{x-1choose i-1}{n-xchoose n-A-i}f_{i+A}) 的部分,采取类似的证法也可以得到类似的结论。

    也就是说洗牌的过程中期望的最高次数不会改变。一开始是 k 次函数,后面依然是 k 次函数。

    @accepted code@

    #include <cstdio>
    #include <algorithm>
    using namespace std;
    
    const int MOD = 998244353;
    const int MAXN = 10000000;
    
    inline int add(int x, int y) {x += y; return x >= MOD ? x - MOD : x;}
    inline int sub(int x, int y) {x -= y; return x < 0 ? x + MOD : x;}
    inline int mul(int x, int y) {return (int)(1LL * x * y % MOD);}
    
    int pow_mod(int b, int p) {
    	int ret = 1;
    	for(int i=p;i;i>>=1,b=mul(b,b))
    		if( i & 1 ) ret = mul(ret, b);
    	return ret;
    }
    
    int read() {
    	int x = 0, ch = getchar();
    	while( ch > '9' || ch < '0' ) ch = getchar();
    	while( '0' <= ch && ch <= '9' ) x = 10*x + ch - '0', ch = getchar();
    	return x;
    }
    void write(int x) {
    	if( x / 10 ) write(x / 10);
    	putchar(x % 10 + '0');
    }
    
    int iv[MAXN + 5];
    void init() {
    	iv[1] = 1; for(int i=2;i<=MAXN;i++) iv[i] = sub(0, mul(MOD / i, iv[MOD % i]));
    }
    int inv(int x) {
    	if( x <= MAXN ) return iv[x];
    	else if( sub(0, x) <= MAXN ) return sub(0, iv[sub(0, x)]);
    	else return pow_mod(x, MOD - 2);
    }
    
    struct point{int x, y;};
    int get(point a, point b, int x) {
    	int p1 = inv(sub(a.x, b.x)), p2 = inv(sub(b.x, a.x));
    	return add(mul(a.y, mul(sub(x, b.x), p1)), mul(b.y, mul(sub(x, a.x), p2)));
    }
    int get(point a, point b, point c, int x) {
    	int p1 = mul(inv(sub(a.x, b.x)), inv(sub(a.x, c.x))), q1 = mul(sub(x, b.x), sub(x, c.x));
    	int p2 = mul(inv(sub(b.x, c.x)), inv(sub(b.x, a.x))), q2 = mul(sub(x, c.x), sub(x, a.x));
    	int p3 = mul(inv(sub(c.x, a.x)), inv(sub(c.x, b.x))), q3 = mul(sub(x, a.x), sub(x, b.x));
    	return add(add(mul(a.y, mul(p1, q1)), mul(b.y, mul(p2, q2))), mul(c.y, mul(p3, q3)));
    }
    
    int ans[MAXN + 5];
    int main() {
    	freopen("landlords.in", "r", stdin);
    	freopen("landlords.out", "w", stdout);
    	
    	init(); int n = read(), m = read(), type = read();
    	if( type == 1 ) {
    		if( n == 1 ) ans[1] = 1;
    		else {
    			point a = (point){1, 1}, b = (point){n, n};
    			for(int i=1;i<=m;i++) {
    				int A = read();
    				if( A == 0 || A == n ) continue;
    				
    				int t1 = add(mul(get(a, b, 1), A), mul(get(a, b, A + 1), n - A));
    				int t2 = add(mul(get(a, b, A), A), mul(get(a, b, n), n - A));
    				a = (point){1, mul(t1, inv(n))}, b = (point){n, mul(t2, inv(n))};
    			}
    			for(int i=1;i<=n;i++) ans[i] = get(a, b, i);
    		}
    	} else {
    		if( n == 1 ) ans[1] = 1;
    		else if( n == 2 ) {
    			bool flag = false;
    			for(int i=1;i<=m;i++)
    				flag |= (read() == 1);
    			if( flag ) ans[1] = ans[2] = mul(5, inv(2));
    			else ans[1] = 1, ans[2] = 4;
    		} else {
    			point a = (point){1, 1}, b = (point){2, 4}, c = (point){n, mul(n, n)};
    			for(int i=1;i<=m;i++) {
    				int A = read();
    				if( A == 0 || A == n ) continue;
    				
    				int p1 = (A == 1) ? 0 : mul(get(a, b, c, 2), mul(A, A - 1));
    				int p2 = mul(get(a, b, c, 1), mul(A, n - A));
    				int p3 = mul(get(a, b, c, A + 1), mul(A, n - A));
    				int p4 = (A == n - 1) ? 0 : mul(get(a, b, c, A + 2), mul(n - A, n - A - 1));
    				
    				int t1 = add(mul(get(a, b, c, 1), A), mul(get(a, b, c, A + 1), n - A));
    				int t2 = add(mul(get(a, b, c, A), A), mul(get(a, b, c, n), n - A));
    				a = (point){1, mul(t1, inv(n))}, b = (point){n, mul(t2, inv(n))};
    				c = (point){2, mul(add(add(p1, p2), add(p3, p4)), mul(inv(n), inv(n - 1)))};
    			}
    			for(int i=1;i<=n;i++) ans[i] = get(a, b, c, i);
    		}
    	}
    	
    	for(int Q=read();Q;Q--)
    		write(ans[read()]), puts("");
    }
    

    @details@

    真就打表就能win啊。
    为什么我去年会在考试时一直想生成函数怎么写啊。

    实现时可以把一次函数与二次函数合在一起写。

    话说都没有人写出题人给的那个 “倒着看是基数排序” 的做法吗。update in 2020/06/09:lk orz

  • 相关阅读:
    设置文本框的 placeholder 的颜色
    CSS单行文字超出省略
    【持续跟新】剑指Offer_Java实现
    Android必修课-Activity生命周期
    如何查看Android的jks签名的MD5
    Flutter 文字边框/边框颜色
    Flutter initState 初始化调用 Provide报错
    flutter 系统通知栏Demo 基于flutter_local_notifications: ^1.4.1
    # Flutter学习笔记(一)
    一个技术人毕业到就业的思考
  • 原文地址:https://www.cnblogs.com/Tiw-Air-OAO/p/13067893.html
Copyright © 2011-2022 走看看