1. 程式人生 > >[NOI2012]迷失遊樂園,洛谷P2081,概率期望+樹上問題進階

[NOI2012]迷失遊樂園,洛谷P2081,概率期望+樹上問題進階

正題

      題目在此

      思考暴搜,好像不行,我們來退一步想。

       樹上的我肯定是會的。

       用down[x]表示x這個節點往下走的期望路徑長度。

       明顯的,轉移方程:down[x]=\frac{\sum_{i\in son_x}down[i]+c(x,i)}{son[x]}

       其中son_x表示x的兒子,son[x]表示x的兒子個數,c(x,i)表示x到i的邊長。

       用up[x]表示這個節點往上走的期望路徑長度。

      轉移方程思索一下就可以的出來:up[x]=c(fa,x)+\frac{up[fa]*(tot[fa]-son[fa])+down[fa]*son[fa]-down[x]-c(fa,x)}{tot[fa]-1}

      是不是眼花繚亂。其中tot[fa]是父親的度(連邊就算)。(特殊討論tot[fa]=1)

      其實很好理解,從x的外部節點走向x是不是一定要經過fa到x的這條邊,所以我們加在外面,另外的,我們算出fa向上走的期望長度乘向上走的路的條數,加上fa向下走的期望長度乘上 向下走的路的條數(加起來相當於從fa向每個方向走的長度乘方向總數)再減去這個子樹帶來的影響,最後除以剩下往外走的路徑條數。

      有點難理解吧,這裡感性一點。

      如果有一個環呢?

      就變成了基環樹。找環我肯定也是會的。處理出每一個節點的down我也是會的。(相當於很多棵樹)

      關鍵是up,怎麼處理一個節點往外走的期望路徑長度。

      顯然對於每一棵小樹我們只需要根節點往外走的期望就足夠了。

      怎麼求根節點往外走的期望長度?

      分兩種情況討論:

       1.順時針走。

       2.逆時針走。

      明顯向兩邊走的機率都是二分之一。

      假設當前點是x,要往外走,那麼up[x]=(down[環上的另一個節點]+x到這個節點的距離)*x到這個節點概率。

      第一個文字可以列舉,第二個文字可以累加路徑,第三個文字“概率”應該怎麼求呢?

      我們經過一個環上節點p之後,繼續往下走的機率是\frac{1}{son[p]+1}。因為其他的機率都分給了這棵樹。

      特殊討論順時針或逆時針的最後一個節點,因為到了這個節點,就不能再往下走了,所有的機率都分給了這一棵樹。這題荒廢了我1個半小時做和40分鐘寫部落格。

  

#include<cstdio>
#include<cstdlib>
#include<cstring>
#include<iostream>
#define ne (now==t?1:now+1)
#define la (now==1?t:now-1)
using namespace std;

int n,m;
struct edge{
	int y,next,c;
}s[200010];
int first[100010],len=0;
bool vis[100010],we=false,tf=false,stack[100010];
bool inlp[100010];
double down[100010],up[100010];
int son[100010];
int loop[100010],tot[100010],dis[100010],t=0;

void ins(int x,int y,int c){
	len++;
	s[len]=(edge){y,first[x],c};first[x]=len;
}

void dfs_1(int x,int fa){
	down[x]=0;
	for(int i=first[x];i!=0;i=s[i].next){
		int y=s[i].y;
		tot[x]++;
		if(y==fa || inlp[y]) continue;
		dfs_1(y,x);
		down[x]+=down[y]+s[i].c;son[x]++;
	}
	if(son[x]!=0) down[x]/=son[x];
}

void dfs_2(int x,int fa){
	for(int i=first[x];i!=0;i=s[i].next){
		int y=s[i].y;
		if(y==fa || inlp[y]) continue;
		up[y]=s[i].c+((tot[x]-son[x])*up[x]+down[x]*son[x]-down[y]-s[i].c)/(tot[x]-1==0?1:tot[x]-1);
		dfs_2(y,x);
	}
}

void find_loop(int x,int fa){
	stack[x]=true;
	for(int i=first[x];i!=0;i=s[i].next){
		int y=s[i].y;
		if(y==fa) continue;
		if(stack[y]){
			we=tf=true;
			loop[++t]=y;
			inlp[y]=true;
		}
		if(!we) find_loop(y,x);
		if(tf){
			if(loop[1]!=x) loop[++t]=x,inlp[x]=true,dis[t]=s[i].c;
			else tf=false,dis[1]=s[i].c;
			break;
		}
		if(we) break;
	}
	stack[x]=false;
}

int main(){
	scanf("%d %d",&n,&m);
	int x,y,c;
	for(int i=1;i<=m;i++){
		scanf("%d %d %d",&x,&y,&c);
		ins(x,y,c);
		ins(y,x,c);
	}
	if(m==n-1) dfs_1(1,0),dfs_2(1,0);
	else{
		find_loop(1,0);
		for(int i=1;i<=t;i++) dfs_1(loop[i],0);
		for(int i=1;i<=t;i++){
			//逆時針 
			double to,di=1.0/2;
			int now=i==t?1:i+1;
			to=dis[now];
			while(now!=i){
				di/=(son[loop[now]]+1);
				up[loop[i]]+=(to+down[loop[now]])*di*(ne==i?son[loop[now]]+1:son[loop[now]]);
				now=ne;
				to+=dis[now];
			}
			//順時針
			di=1.0/2;
			now=i==1?t:i-1;
			to=dis[i];
			while(now!=i){
				di/=(son[loop[now]]+1);
				up[loop[i]]+=(to+down[loop[now]])*di*(la==i?son[loop[now]]+1:son[loop[now]]);
				to+=dis[now];
				now=la;
			}
		}
		for(int i=1;i<=t;i++) dfs_2(loop[i],0);
	}
	double ans=0;
	for(int i=1;i<=n;i++) ans+=((tot[i]-son[i])*up[i]+son[i]*down[i])/tot[i];
	printf("%.6lf",ans/n);
}