zoukankan      html  css  js  c++  java
  • [ZJOI2015]地震后的幻想乡

    VII.[ZJOI2015]地震后的幻想乡

    本题有两种思路。

    一种思路是从暴力入手并优化状态。

    我们考虑边的一组排列\(\{p_1,\dots,p_m\}\)。它是将边按照边权从小到大排列的结果。则我们在这组排列上跑Kruskal,设在加入排名为\(i\)的边时跑出了一棵生成树,则这组排列的答案就是排名为\(i\)的边权期望,按照题面中的提示,它就是\(\dfrac{i}{m+1}\)

    枚举每一种排列——它们都是等概率出现的——就能求出总概率。则这个算法的时间复杂度是\(O(m\times m!)\)

    考虑优化。因为我们要求的是生成树中最大边,所以多加入一些边并不会影响最大边的大小,于是我们实际上只要找出一个使得之前图不连通,在加入这条边后图联通了的位置\(i\)即可。

    于是,我们发现在我们Kruskal加入一条新边之时,我们只考虑之前放了多少条边以及图的连通性即可。

    发现单独求出加入多少条边时刚好联通不好求;于是我们干脆做个后缀和,设\(f[i][j]\)表示加入\(i\)条边后集合\(j\)联通的方案数,最后做一个差分即可求出所有刚好联通的方案数。

    发现\(f[i][j]\)不好求。于是我们采取正难则反,考虑设\(g[i][j]\)表示集合\(j\)不连通的方案数。

    我们考虑如何求出\(j\)。一个显然的想法是\(O(3^n)\)枚举子集,将\(j\)集合切成两半处理。为了不重不漏地计数,我们就强制令一半联通,另一半随便连,同时两半间不连边。这里我们采取计数问题中的经典策略,找出\(j\)中的lowbit位,设为\(p\),并且强制令联通的集合中必须包含\(p\)(这就相当于枚举\(p\)所在的连通块)。

    我们设\(|j|\)表示\(j\)集合内部共有多少条边。则我们有

    \[g_{i,j}=\sum\limits_{k\subset j,p\in k}\sum\limits_{l=0}^if_{l,k}\times\dbinom{\Big|j\setminus k\Big|}{i-l} \]

    其中,\(k\)枚举子集,\(l\)枚举\(k\)中有多少条边,二项式系数的意义是从集合\(j\setminus k\)(其中\(\setminus\)是集合减符号)中选出\(i-l\)条边的方案数。

    另,我们又有

    \[f_{i,j}+g_{i,j}=\dbinom{|j|}{i} \]

    这很好理解,因为联通数加上不连通数必定等于总方案数。故我们就可以直接通过该式求出\(f_{i,j}\)

    则我们设\(h_i\)表示加入\(i\)条边时整张图联通的概率。于是有

    \[h_i=\dfrac{f_{i,\mathbb{V}}}{\binom{m}{i}} \]

    其中\(\mathbb{V}\)是全体点集。

    \(h_i\)做差分就得到位置\(i\)刚好联通的概率;概率再乘上权值(\(\dfrac{i}{m+1}\))就得到了期望。

    时间复杂度\(O(m^23^n)\)

    代码:

    #include<bits/stdc++.h>
    using namespace std;
    typedef long long ll;
    int n,m,lim;
    bool s[20][20];
    ll f[100][1<<10],g[100][1<<10],C[100][100],sz[1<<10];
    double h[100],res;
    int main(){
    	scanf("%d%d",&n,&m),lim=1<<n;
    	for(int i=0;i<=m;i++)C[i][0]=1;
    	for(int i=1;i<=m;i++)for(int j=1;j<=i;j++)C[i][j]=C[i-1][j-1]+C[i-1][j];
    	for(int i=1,x,y;i<=m;i++)scanf("%d%d",&x,&y),x--,y--,s[x][y]=s[y][x]=true;
    	
    	for(int i=0;i<lim;i++)g[0][i]=1;
    	for(int i=0;i<n;i++)f[0][1<<i]=1,g[0][1<<i]=0;
    	for(int i=1;i<lim;i++){
    		sz[i]=sz[i^(i&-i)];
    		int p=__builtin_ctz(i);
    		for(int j=0;j<n;j++)if(i&(1<<j))sz[i]+=s[j][p];
    	}
    	
    	for(int i=1;i<=m;i++)for(int j=1;j<lim;j++){
    		int p=__builtin_ctz(j);
    		for(int k=(j-1)&j;k;k=(k-1)&j)if(k&(1<<p))for(int l=0;l<=i;l++)g[i][j]+=f[l][k]*C[sz[j^k]][i-l];
    		f[i][j]=C[sz[j]][i]-g[i][j];
    	}
    	
    	for(int i=0;i<=m;i++)h[i]=1.0*f[i][lim-1]/C[m][i];
    	for(int i=m;i>=1;i--)h[i]-=h[i-1];
    	for(int i=0;i<=m;i++)res+=h[i]*i;
    	
    	printf("%lf\n",res/(m+1));
    	return 0;
    } 
    

    另一种做法是从积分角度分析。

    我们设\(p(x)\)为最大边为\(x\)的概率。则我们要求的期望则为

    \[\mathrm{E}X=\int_0^1p(x)x\mathrm{d}x \]

    仿照前一种思路,我们设 \(P(X)=\int_X^1p(x)\mathrm{d}x\),则有

    \[\begin{aligned}\mathrm{E}X=&\int_0^1p(x)x\mathrm{d}x\\=&\int_0^1p(x)\Big(\int_0^x1\mathrm{d}t\Big)\mathrm{d}x\\=&\int_0^1\Big(\int_t^1p(x)\mathrm{d}x\Big)\mathrm{d}t\\=&\int_0^1P(t)\mathrm{d}t\end{aligned} \]

    下面我们考虑如何求出上式。观察到\(P(X)\)的实际意义是最大边大于等于\(X\)的概率;于是我们设\(P_{\mathbb{S}}(X)\)表示\(\mathbb{S}\)集合中最大边大于等于\(X\)的概率,则就有\(P(X)=P_{\mathbb{V}}(X)\),其中\(\mathbb{V}\)是全部节点集合。

    我们考虑转移出\(P_{\mathbb{S}}(X)\):我们这次考虑枚举\(1\)所在的那个连通块,设此连通块为\(\mathbb{T}\),则\(\mathbb{T}\)联通的概率是\(1-P_{\mathbb{T}}(X)\)\(\mathbb{T}\)不与其它部分联通的概率是\((1-X)^{e(\mathbb{T},\mathbb{S}-\mathbb{T})}\),其中\(e(\mathbb{T},\mathbb{S}-\mathbb{T})\)\(\mathbb{S}\)与其补集间边数。

    于是就有

    \[P_{\mathbb{S}}(X)=\sum\limits_{1\in\mathbb{T}\subset\mathbb{S}}\Big(1-P_{\mathbb{T}}(X)\Big)(1-X)^{e(\mathbb{T},\mathbb{S}-\mathbb{T})} \]

    考虑到我们最终要求的是\(\int_0^1P_{\mathbb{V}}(X)\mathrm{d}X\);于是开始推式子:

    \[\begin{aligned}\int_0^1P_{\mathbb{S}}(X)\mathrm{d}X&=\int_0^1\Bigg(\sum\limits_{1\in\mathbb{T}\subset\mathbb{S}}\Big(1-P_{\mathbb{T}}(X)\Big)(1-X)^{e(\mathbb{T},\mathbb{S}-\mathbb{T})}\Bigg)\mathrm{d}X\\&=\int_0^1\Bigg(\sum\limits_{1\in\mathbb{T}\subset\mathbb{S}}(1-X)^{e(\mathbb{T},\mathbb{S}-\mathbb{T})}-P_{\mathbb{T}}(X)(1-X)^{e(\mathbb{T},\mathbb{S}-\mathbb{T})}\Bigg)\mathrm{d}X\\&=\sum\limits_{1\in\mathbb{T}\subset\mathbb{S}}\int_0^1(1-X)^{e(\mathbb{T},\mathbb{S}-\mathbb{T})}\mathrm{d}X-\int_0^1P_{\mathbb{T}}(X)(1-X)^{e(\mathbb{T},\mathbb{S}-\mathbb{T})}\mathrm{d}X\\&=\sum\limits_{1\in\mathbb{T}\subset\mathbb{S}}\int_0^1X^{e(\mathbb{T},\mathbb{S}-\mathbb{T})}\mathrm{d}X-\int_0^1P_{\mathbb{T}}(X)(1-X)^{e(\mathbb{T},\mathbb{S}-\mathbb{T})}\mathrm{d}X\\&=\sum\limits_{1\in\mathbb{T}\subset\mathbb{S}}\dfrac{1^{e(\mathbb{T},\mathbb{S}-\mathbb{T})}}{1+e(\mathbb{T},\mathbb{S}-\mathbb{T})}-\dfrac{0^{e(\mathbb{T},\mathbb{S}-\mathbb{T})}}{1+e(\mathbb{T},\mathbb{S}-\mathbb{T})}-\int_0^1P_{\mathbb{T}}(X)(1-X)^{e(\mathbb{T},\mathbb{S}-\mathbb{T})}\mathrm{d}X\\&=\sum\limits_{1\in\mathbb{T}\subset\mathbb{S}}\dfrac{1}{1+e(\mathbb{T},\mathbb{S}-\mathbb{T})}-\int_0^1P_{\mathbb{T}}(X)(1-X)^{e(\mathbb{T},\mathbb{S}-\mathbb{T})}\mathrm{d}X\end{aligned} \]

    到这里,我们停一下,设一个

    \[F^k(\mathbb{S})=\int_0^1(1-X)^kP_{\mathbb{S}}(X)\mathrm{d}X \]

    则代入上面的式子,就有

    \[F^0(\mathbb{S})=\sum\limits_{1\in\mathbb{T}\subset\mathbb{S}}\dfrac{1}{1+e(\mathbb{T},\mathbb{S}-\mathbb{T})}-F^{e(\mathbb{T},\mathbb{S}-\mathbb{T})}(\mathbb{T}) \]

    于是现在的目标就是求出任意的\(F^k(\mathbb{S})\);这很简单,只需要类似地代入式子中强推即可得到

    \[F^k(\mathbb{S})=\sum\limits_{1\in\mathbb{T}\subset\mathbb{S}}\dfrac{1}{1+k+e(\mathbb{T},\mathbb{S}-\mathbb{T})}-F^{k+e(\mathbb{T},\mathbb{S}-\mathbb{T})}(\mathbb{T}) \]

    显然这就可以\(O(m3^n)\)地DP了。最终答案即为\(F^0(\mathbb{V})\)

    代码(极致压行版——它甚至没有我一个推导的式子长):

    #include<stdio.h>
    int n,m,in[1<<10],lim;
    double f[1<<10][100];
    int main(){
    	scanf("%d%d",&n,&m),lim=1<<n;
    	for(int i=1,x,y;i<=m;i++){scanf("%d%d",&x,&y),x--,y--;for(int j=0;j<lim;j++)if((j&(1<<x))&&(j&(1<<y)))in[j]++;}
    	for(int i=0;i<lim;i++)if(i&1)for(int j=(i-1)&i;j;j=(j-1)&i)if(j&1)for(int k=0,l=in[i]-in[j]-in[i^j];k+l<=m;k++)f[i][k]+=1.0/(k+l+1)-f[j][k+l];
    	printf("%.6lf\n",f[lim-1][0]);
    	return 0;
    } 
    

  • 相关阅读:
    C++ 单例模式
    单链表快速排序
    美团后台面经
    排序算法及其优化总结
    (转)再谈互斥量与环境变量
    互斥锁和自旋锁
    算法题总结----数组(二分查找)
    Linux里的2>&1的理解
    Ubuntu下开启mysql远程访问
    说说eclipse调优,缩短启动时间
  • 原文地址:https://www.cnblogs.com/Troverld/p/14610919.html
Copyright © 2011-2022 走看看