[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
#include
#include
#include
#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);
}

 

你可能感兴趣的:([NOI2012]迷失游乐园,洛谷P2081,概率期望+树上问题进阶)