• [JZOJ6340] 【NOIP2019模拟2019.9.4】B


    题目

    题目大意

    给你个非负整数数列(a),每次等概率选择大于零的(a_i),使其减(1)
    (a_1)被减到(0)的时候期望经过多少次操作。


    思考历程

    对于这题的暴力做法,显然可以状态压缩吧……
    然后我突然意识到,实际上我们将题目转化成以下模型:
    (n)种颜色,第(i)种颜色的小球有(a_i)个。那么题目就变成了一个有重复元素的排列问题。
    先将(2)(n)排列求出来,然后考虑将(1)随机插入。
    枚举最后一个(1)出现的位置,然后在前面用组合数计算插入的方案数。
    这个做法打出来之后一直没有调出来,也不知道是我数学功底不好还是这个方法本来就是错的……
    当然,即便是对的,也不会是满分做法。


    正解

    根据期望的线性性,题目可以转化为:对于每个(i>1)(a_1)减到(0)的时候(a_i)期望取了多少个。然后将每个(i)的这东西加起来,再加上(a_1),就是答案。
    于是我们就只需要考虑这个问题。
    接着就是最骚的操作:我们只考虑(a_1)(a_i),其它被减掉对它们没有影响。它们有相等的概率被减(1),这个概率可以看做是(frac{1}{2})。于是相当于题目中(n=2)的情况。
    现在考虑一个平面,一开始的坐标为((a_1,a_i)),每次随机地向下面或者左边走一格。
    如果到达边界((0,y)),则会有(a_i-y)的贡献;如果到达边界((x,0)),则会有(a_i)的贡献。
    对于边界((0,y)),设向下走了(j)步,则概率为(frac{C_{a_1+j-1}^{j}}{2^{a_1+j}})
    对于边界((x,0)),既然它们的贡献都一样,不妨将上面的和用(1)减掉,就是它的概率。
    接着就可以列出一个式子……
    列出之后容易发现,((a_1,a_i+1))的答案可以(O(1))地从((a_i,a_i))转移过来。


    代码

    using namespace std;
    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #include <cassert>
    #define N 500010
    #define A 500010
    #define mo 323232323
    inline int input(){
    	char ch=getchar();
    	while (ch<'0' || '9'<ch)
    		ch=getchar();
    	int x=0;
    	do{
    		x=x*10+ch-'0';
    		ch=getchar();
    	}
    	while ('0'<=ch && ch<='9');
    	return x;
    }
    int n,a[N];
    int fac[N+A],invf[N+A],inv2[N+A];
    int ans1[A*3],ans2[A*3],ans[A*3];
    inline int inv(int x){
    	int res=1;
    	for (int y=mo-2;y;y>>=1,x=(long long)x*x%mo)
    		if (y&1)
    			res=(long long)res*x%mo;
    	return res;
    }
    inline int C(int m,int n){return (long long)fac[m]*invf[n]%mo*invf[m-n]%mo;}
    int main(){
    	freopen("b.in","r",stdin);
    	freopen("b.out","w",stdout);
    	n=input();
    	for (int i=1;i<=n;++i)
    		a[i]=input();
    	fac[0]=1,inv2[0]=1;
    	for (int i=1;i<=A*2;++i){
    		fac[i]=(long long)fac[i-1]*i%mo;
    		inv2[i]=(long long)inv2[i-1]*2%mo;
    	}
    	for (int i=0;i<=A*2;++i){
    		invf[i]=inv(fac[i]);
    		inv2[i]=inv(inv2[i]);
    	}
    	ans1[0]=ans2[0]=0;
    	for (int i=1;i<=A;++i){
    		ans1[i]=(ans1[i-1]+(long long)C(a[1]+i-2,i-1)*(i-1)%mo*inv2[a[1]+i-1]%mo)%mo;
    		ans2[i]=(ans2[i-1]+(long long)C(a[1]+i-2,i-1)*inv2[a[1]+i-1]%mo)%mo;
    		ans[i]=(ans1[i]+(long long)i*(1-ans2[i]+mo)%mo)%mo;
    	}
    	long long sum=a[1];
    	for (int i=2;i<=n;++i)
    		sum+=ans[a[i]];
    	printf("%lld
    ",sum%mo);
    	return 0;
    }
    

    总结

    期望的线性性真是奇妙啊……

  • 相关阅读:
    如何使用Orchard搭建敏捷个人的网站(1)
    英语:敏捷英语学习开始了
    英语:普特三步听写法(转载)
    色拉英语第一集第五幕:好胖的一只鸟
    介绍一个基于ASP.NET MVC的框架Catharsis
    色拉英语第2集第3幕:He’s my favorite
    Orchard:如何生成Hello World模块
    如何使用Orchard搭建敏捷个人的网站(2)
    色拉英语第一集第四幕:我不喜欢北京烤鸭
    色拉英语第一集第二幕:请问南京路怎么走?
  • 原文地址:https://www.cnblogs.com/jz-597/p/11483219.html
Copyright © 2020-2023  润新知