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]!='\0';++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\n",ans);
  return 0;
}
posted @ 2017-05-03 18:42 liu_runda 阅读(...) 评论(...) 编辑 收藏
偶然想到可以用这样的字体藏一点想说的话,可是并没有什么想说的. 现在有了:文化课好难