[NOI2012]迷失遊樂園,洛谷P2081,概率期望+樹上問題進階
正題
題目在此。
思考暴搜,好像不行,我們來退一步想。
樹上的我肯定是會的。
用down[x]表示x這個節點往下走的期望路徑長度。
明顯的,轉移方程:。
其中表示x的兒子,表示x的兒子個數,表示x到i的邊長。
用up[x]表示這個節點往上走的期望路徑長度。
轉移方程思索一下就可以的出來:
是不是眼花繚亂。其中是父親的度(連邊就算)。(特殊討論tot[fa]=1)
其實很好理解,從x的外部節點走向x是不是一定要經過fa到x的這條邊,所以我們加在外面,另外的,我們算出fa向上走的期望長度乘向上走的路的條數,加上fa向下走的期望長度乘上 向下走的路的條數(加起來相當於從fa向每個方向走的長度乘方向總數)再減去這個子樹帶來的影響,最後除以剩下往外走的路徑條數。
有點難理解吧,這裡感性一點。
如果有一個環呢?
就變成了基環樹。找環我肯定也是會的。處理出每一個節點的down我也是會的。(相當於很多棵樹)
關鍵是up,怎麼處理一個節點往外走的期望路徑長度。
顯然對於每一棵小樹我們只需要根節點往外走的期望就足夠了。
怎麼求根節點往外走的期望長度?
分兩種情況討論:
1.順時針走。
2.逆時針走。
明顯向兩邊走的機率都是二分之一。
假設當前點是x,要往外走,那麼up[x]=(down[環上的另一個節點]+x到這個節點的距離)*x到這個節點概率。
第一個文字可以列舉,第二個文字可以累加路徑,第三個文字“概率”應該怎麼求呢?
我們經過一個環上節點p之後,繼續往下走的機率是。因為其他的機率都分給了這棵樹。
特殊討論順時針或逆時針的最後一個節點,因為到了這個節點,就不能再往下走了,所有的機率都分給了這一棵樹。這題荒廢了我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);
}