正题
题目链接:https://www.luogu.com.cn/problem/P5516
题目大意
nnn个字母的一个字符串,每次随机选取两个不同的位置(x,y)(x,y)(x,y)让第xxx个位置的字符变成第yyy个位置的字符。
求期望多少次能够把所有字符变成同一个。
解题思路
因为最终状态很多,所以我们考虑枚举最终状态,我们称之为目标状态。
设pip_ipi表示目标状态的字符有iii个时,该状态变为最终状态的概率,那么有pi=i(n−i)n(n−1)pi−1+n(n−1)−2i(n−i)n(n−1)+i(n−i)n(n−1)pi+1⇒pi=pi−1+pi+12p_i=\frac{i(n-i)}{n(n-1)}p_{i-1}+\frac{n(n-1)-2i(n-i)}{n(n-1)}+\frac{i(n-i)}{n(n-1)}p_{i+1}\Rightarrow p_i=\frac{p_{i-1}+p_{i+1}}{2}pi=n(n−1)i(n−i)pi−1+n(n−1)n(n−1)−2i(n−i)+n(n−1)i(n−i)pi+1⇒pi=2pi−1+pi+1
我们观察一下这个式子,其实等价于pi−pi−1=pi+1−pip_i-p_{i-1}=p_{i+1}-p_{i}pi−pi−1=pi+1−pi表示ppp是一个等差数列,而p0=0,pn=1p_{0}=0,p_{n}=1p0=0,pn=1所以pi=inp_i=\frac{i}{n}pi=ni
然后设fif_ifi表示目标状态有iii个且是最终状态的情况下到达最终状态的期望步数,而因为 数学期望=概率×步数 ,和 单次期望=1单次概率\frac{1}{单次概率}单次概率1
所以我们成功造成一次变化的期望次数是12i(n−i)n(n−1)\frac{1}{\frac{2i(n-i)}{n(n-1)}}n(n−1)2i(n−i)1,又因为多和少的概率是相等的,所以有方程
fipi=pin(n−1)2i(n−i)+pi+1fi+1+pi−1fi−12f_ip_i=p_i\frac{n(n-1)}{2i(n-i)}+\frac{p_{i+1}f_{i+1}+p_{i-1}f_{i-1}}{2}fipi=pi2i(n−i)n(n−1)+2pi+1fi+1+pi−1fi−1
因为知道pi=inp_i=\frac{i}{n}pi=ni所以直接带进去解出来就有
fi=n(n−1)2i(n−i)+i−12ifi−1+i+12ifi+1f_i=\frac{n(n-1)}{2i(n-i)}+\frac{i-1}{2i}f_{i-1}+\frac{i+1}{2i}f_{i+1}fi=2i(n−i)n(n−1)+2ii−1fi−1+2ii+1fi+1
换一个方式表达就是
(−i−12ifi−1)+(fi)+(−i+12ifi+1)=n(n−1)2i(n−i)(-\frac{i-1}{2i}f_{i-1})+(f_i)+(-\frac{i+1}{2i}f_{i+1})=\frac{n(n-1)}{2i(n-i)}(−2ii−1fi−1)+(fi)+(−2ii+1fi+1)=2i(n−i)n(n−1)
又有fn=0f_n=0fn=0
这样问题就变成了n−1n-1n−1个线性方程的方程组,因为每个方程组只有三个元,高斯消元会有大量多余步骤,所以我们直接手动先消掉fi−1f_{i-1}fi−1再返回来消掉fi+1f_{i+1}fi+1就好了。
时间复杂度O(n)O(n)O(n)
codecodecode
#include<cstdio>
#include<cstring>
#include<algorithm>
using namespace std;
const int N=2100;
int n,num[26];
double f[N],a[N],ans;
char s[N];
int main()
{scanf("%s",s+1);n=strlen(s+1);for(int i=1;i<=n;i++)num[s[i]-'A']++;for(int i=1;i<=n;i++)f[i]=n*(n-1)/(2.0*i*(n-i));a[1]=1;for(int i=1;i<n;i++){double r=-(i+1)/(2.0*i),l=-i/(2.0*(i+1));double k=-l/a[i];f[i+1]+=f[i]*k;a[i+1]=1+k*r;}f[n]=0;for(int i=n-1;i>=1;i--){double r=-(i+1)/(2.0*i);f[i]-=f[i+1]*r,f[i]=f[i]/a[i];}for(int i=0;i<26;i++)ans+=num[i]*f[num[i]]/(double)n;printf("%.1lf\n",ans);
}