zoukankan      html  css  js  c++  java
  • bzoj2554 Color

    题意:

    有n个球排成一列,每个球都有一个颜色,用A-Z的大写字母来表示,我们每次随机选出两个不同的球ball1,ball2(有(n*(n-1))种选法,这里两个球的顺序是有用的),使得后者染上前者的颜色(两个球的颜色可以相同).
    求期望操作多少次,才能使得所有球的颜色都一样?

    分析:

    我们从比较简单的情况开始考虑.比如只有两种颜色,黑和白.那么当前的状态可以由白球的个数唯一确定.
    f[i]表示当前有i个白球的时候变到所有球的颜色都一样的期望步数,那么边界为(f[0]=0,f[n]=0.)
    注意进行一次操作后,白球的数目不一定发生变化.假如现在有i个白球,那么一共有(n*(n-1))种选法,但只有(2*i*(n-i))种选法会使白球的数目发生变化.
    也就是说,当前有i个白球的时候,进行一次操作使得白球的数目发生变化的概率P[i]可以直接算出来,是发生变化的方案数/总方案数
    那么有i个白球的时候,根据一个在概率期望题里常用的结论,期望操作1/P[i]次之后会使白球的数目发生变化.记(g[i]=1/P[i])
    还有一个结论:如果某次操作使得白球的数目变化了,那么白球的数目+1和-1的概率都是0.5 显然,这时把一个白球变黑的方案数等于把一个黑球变白的方案数,均为(i*(n-i))
    那么,对于1<=i<=n-1的i,(f[i]=g[i]+0.5*f[i-1]+0.5*f[i+1])
    这样的方程组,不需要高斯消元求解,可以推推式子然后O(n)递推出来(类似今年的HEOI2017Day2T2).
    具体推法是:
    (f[1]=g[1]+0.5*f[0]+0.5*f[2]=g[1]+0.5*f[2])
    于是我们把f[1]表示成了(k[1]*f[2]+b[1])的形式,其中k[1],b[1]都已经求出来了.
    (f[2]=g[2]+0.5*f[1]+0.5*f[3])
    之前我们把(f[1])表示成了(k[1]*f[2]+b[1])的形式,现在就可以在f[2]的表达式里代入(f[1]=k[1]*f[2]+b[1])
    经过整理,就可以把(f[2])表示成(k[2]*f[3]+b[2])的形式.
    如此递推下去,最终可以把(f[n-1])表示为(k[n-1]*f[n]+b[n-1])的形式,而f[n]=0,此时便可依次回代,求出所有的f.
    然而我们有26种颜色....
    考虑把2种颜色的方法拓展.如果从'A'到'Z'枚举最终的颜色,那么就可以把所有球分成两类,和所枚举的最终颜色不同的球都可以认为是颜色相同的.我们求出最终的颜色是'A','B','C'....'Z'的概率,然后分别求出最终颜色为'A','B','C'...'Z'时的期望步数,就可以方便地算出答案.
    那么问题变成了两问:

    1. 最终的颜色是'A','B','C'....'Z'的概率
    2. 最终颜色为'A','B','C'...'Z'时的期望步数

    第1问:

    因为已经枚举了一种最终的颜色,假如这种最终颜色的球现在有i个,那么问题相当于有i个白球,n-i个黑球,最终所有球变为白色的概率.
    记这个概率为h[i].可以发现对26种颜色,需要的h数组是相同的,所以h数组只需要求一遍.
    那么边界(h[0]=0,h[n]=1)
    转移方程是(h[i]=0.5*h[i-1]+0.5*h[i+1])
    同样可以用之前递推f[]的方法O(n)解方程组.实际上,解出来的h[i]=i/n,如果看出来这个规律就可以O(1)直接计算每个h[i],如果没看出来写个O(n)解方程组也没事
    (upd:这个规律其实非常有理有据,h[0]=0,h[n]=1,h[i]为等差数列)

    第2问:

    这一问是有点坑的.因为我们要求的是"最终颜色为A/B/C/D...Z的条件下,期望的步数",那么这里是一个条件概率.
    按照和第1问一样的思路,问题可以转化为"n个球有i个白球,n-i个黑球,假如最后变为全白,那么求走过的步数的期望f[i]",同样这里的f[i]对26种颜色都是一样的.
    边界(f[n]=0),此时f[0]没有意义.如果没有白球,无论如何也变不成全白.
    一开始我没有注意到条件概率...把之前不要求结果为黑/白的f[i]的方程组中(f[1]=g[1]+0.5*f[0]+0.5*f[2])改成了(f[1]=g[1]+f[2]),认为强制不能从f[0]转移过来就能解决问题.
    然而这样完全是错的....这么算根本就没有什么实际意义....我们需要条件概率.
    为啥是错的?
    有i个白球的时候,随机一个操作,白球的个数+1/-1的概率都是0.5,是因为我们考虑所有可能的无限长的随机操作序列,在这些操作序列中恰好一半白球+1,一半白球-1
    但是要求"最终变为全白",概率就不一定是0.5了.不是所有无限长操作序列都使得最终变为全白,所以要用到条件概率.
    关键在于:有i+1个白球和i-1个白球的状态最终能够达到全白状态的概率是不同的,分别是(i+1)/2n和(i-1)/2n.因此,可以大致理解为:如果我们多次重复做这样的操作(每次都是从i个白球开始随机操作直到所有球的颜色都相同),每(i+1)+(i-1)=2i次到达全白状态的结果中,平均有(i+1)次是第一步从i个白球变成i+1个白球,有i-1次是第一步从i个白球变成i-1个白球.
    因此方程应该写作
    (f[i]=g[i]+(i+1)/(2i)*f[i+1]+(i-1)/(2i)*f[i-1])
    这样再O(n)解方程组就没问题了.
    看榜上很多人代码很短跑得很快,感觉最后这个O(n)解方程组应该也能推出O(1)的式子?然而我并不会QAQ,谁来教教我啊.
    2018.6.22 upd:我之前竟然没粘代码?怪不得在搜索引擎上排得那么靠后-_-

    #include<cstdio>
    #include<cstring>
    char buf[10005];
    int cnt[26];
    double g[10005];
    double f[10005],k[10006],b[10005];
    int main(){
      scanf("%s",buf);
      for(int i=0;buf[i]!='';++i)cnt[buf[i]-'A']++;
      int n=strlen(buf);
      for(int i=1;i<n;++i){
        g[i]=(n*n-n)/2.0/i/(n-i);
      }
      //  g[1]=(n*n-1*(n-1))/double(n-1);
      f[n]=0;
      k[1]=1;b[1]=g[1];
      for(int i=2;i<n;++i){
        b[i]=b[i-1]*(i-1)/2.0/i+g[i];
        k[i]=(i+1)/2.0/i;
        k[i]=k[i]/(1-k[i-1]*(i-1)/2.0/i);
        b[i]=b[i]/(1-k[i-1]*(i-1)/2.0/i);
      }
      for(int i=n-1;i>=1;--i){
        f[i]=f[i+1]*k[i]+b[i];
      }
      double ans=0;
      for(int i=0;i<26;++i){
        ans+=cnt[i]/double(n)*f[cnt[i]];
      }
      printf("%.1f
    ",ans);
      return 0;
    }
    
    
  • 相关阅读:
    java中a++和++a的区别详解
    Oracle 对比两张表不一样 的数据
    通配符的匹配很全面, 但无法找到元素 'tx:annotation-driven' 的声明
    Java语言基础-运算符
    java中+=详解 a+=b和a=a+b的区别
    java语言基础-变量
    java语言基础-进制
    Spring整合CXF发布及调用WebService
    Oracle Job定时任务的使用详解
    MySQL的主从配置
  • 原文地址:https://www.cnblogs.com/liu-runda/p/6803126.html
Copyright © 2011-2022 走看看