1. 程式人生 > >bzoj 2878: [Noi2012]迷失遊樂園

bzoj 2878: [Noi2012]迷失遊樂園

什麽 選擇 現在 n-1 hint lar 多少 click cli

Description

放假了,小Z覺得呆在家裏特別無聊,於是決定一個人去遊樂園玩。進入遊樂園後,小Z看了看遊樂園的地圖,發現可以將遊樂園抽象成有n個景點、m條道路的無向連通圖,且該圖中至多有一個環(即m只可能等於n或者n-1)。小Z現在所在的大門也正好是一個景點。小Z不知道什麽好玩,於是他決定,從當前位置出發,每次隨機去一個和當前景點有道路相連的景點,並且同一個景點不去兩次(包括起始景點)。貪玩的小Z會一直遊玩,直到當前景點的相鄰景點都已經訪問過為止。小Z所有經過的景點按順序構成一條非重復路徑,他想知道這條路徑的期望長度是多少?小Z把遊樂園的抽象地圖畫下來帶回了家,可是忘了標哪個點是大門,他只好假設每個景點都可能是大門(即每個景點作為起始點的概率是一樣的)。同時,他每次在選擇下一個景點時會等概率地隨機選擇一個還沒去過的相鄰景點。

Input

第一行是兩個整數n和m,分別表示景點數和道路數。 接下來行,每行三個整數Xi, Yi, Wi,分別表示第i條路徑的兩個景點為Xi, Yi,路徑長Wi。所有景點的編號從1至n,兩個景點之間至多只有一條道路。

Output

共一行,包含一個實數,即路徑的期望長度,保留五位小數

Sample Input

4 3
1 2 3
2 3 1
3 4 4

Sample Output

6.00000

【樣例解釋】樣例數據中共有6條不同的路徑: 路徑 長度 概率
1-->4 8 1/4
2-->1 3 1/8
2-->4 5 1/8
3-->1 4 1/8
3-->4 4 1/8
4-->1 8 1/4
因此期望長度 = 8/4 + 3/8 + 5/8 + 4/8 + 4/8 + 8/4 = 6.00
【評分方法】本題沒有部分分,你程序的輸出只有和標準答案的差距不超過0.01時,才能獲得該測試點的滿分,否則不得分。
【數據規模和約定】對於100%的數據,1 <= Wi <= 100。 測試點編號 n m 備註
1 n=10 m = n-1 保證圖是鏈狀
2 n=100 只有節點1的度數大於2
3 n=1000 /
4 n=100000 /
5 n=100000 /
6 n=10 m = n /
7 n=100 環中節點個數<=5
8 n=1000 環中節點個數<=10
9 n=100000 環中節點個數<=15
10 n=100000 環中節點個數<=20

HINT

Source

這個題真的好迷啊,但是這個題的暴力分是真的沒得說。。。

%爛lcf2000。。。

該題要求的實際上就是從每個點出發走到一個葉子結點的長度的期望(不能走回頭路)。。。

首先考慮樹的情況,考慮和求樹的直徑一樣的dp方法,先處理子樹內部的,再處理子樹外的。。。

設down[i]表示從i這個點往子樹內部走到葉子結點的長度的期望,son[i]表示i這個節點的兒子節點個數。。。

考慮期望的含義就是所有情況的總和除以總情況(就是對所有情況求平均)

我們可以得到轉移:

技術分享

這樣我們就把每個點往下走到葉子節點的期望就出來了。。。

然後我們考慮他通過他的父親節點走出去然後再到子樹外的葉子節點的期望。。。

設up[i]為i節點,往子樹外面走到葉子節點的期望長度。。。

我們考慮向上走的兩種情況:

第一種,節點i走到父親f,然後再走到f的子樹內部(除去i自身的子樹)的其余葉子節點;

這種情況的總長度為:

技術分享

第二種,節點i走到父親f,然後從f繼續往上走,那麽這種情況只有一種:

即為:技術分享

那麽總共有技術分享種情況(f的子樹內部要除去i的子樹,然後再加上往上走的)

那麽我們可以得到轉移:

技術分享

恩,還是很妙的。。。

每個點最後的期望就是:

技術分享

由一些關於根節點的小特判,以及上述所有的除法操作一定要判分母是否為0。。。

然後考慮環套樹的情況,我們可以想象成是一個根結點為一個環的樹,也可以看成環上的每個點都長出去一棵樹。。。

那麽和樹的情況唯一不同的地方就在於,一棵樹上的點到了環上之後,可以通過環,走到別的樹上去。。。

但是環上的點很少,那麽我們可以暴搞。。。

那麽我們可以對於環上的每個點先都把以它為根長出去的樹的down值求出來,這個的求法和樹的情況完全一樣。。。

接下來我們就要處理到了環上之後再走到環上別的點的樹上的情況。。。

那麽環上有兩種走法,一種是順時針,一種是逆時針。。。

環上的點編號為1、2、3、4、5,那麽對於1來說,順時針走的話,

走到2的概率為1,

走到3的概率為技術分享

走到4的話就再乘技術分享……逆時針走的話同理,用g表示到這個點的概率(g的初值為0.5,順時針和逆時針兩種)

同時我們沿著環每走到一個位置就加上從這裏向外向樹走的期望長度,,設d為在環上已經走的路徑。。。

轉移的仍是用總和/總情況。。。

技術分享

(註意繞一圈走到頭的地方與之前的不一樣,因為出發點不可能經過兩次,

技術分享

最後我們改一下前面樹dp的一些定義,用f[i]表示i的父節點個數,默認環上的點i的f[i]=2,然後有的跟父親的轉移稍微改一下。。。

這個題果然是迷失心智。。。

// MADE BY QT666
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<iostream>
#include<cstring>
using namespace std;
typedef long long ll;
const int N=300050;
int cir[N],b[N],dis[N],son[N],f[N],fa[N],vis[N],tt;
int head[N],to[N],nxt[N],w[N],dfn[N],cnt,n,m,len;
double up[N],down[N],ans;
void lnk(int x,int y,int z){
  to[++cnt]=y,nxt[cnt]=head[x],w[cnt]=z,head[x]=cnt;
  to[++cnt]=x,nxt[cnt]=head[y],w[cnt]=z,head[y]=cnt;
}
void dfs_down(int u){
  int tot=0;vis[u]=1;
  for(int i=head[u],v;v=to[i],i;i=nxt[i])
    if(!vis[v]) dis[v]=w[i],dfs_down(v),tot++,down[u]+=down[v]+w[i];
  if(tot) down[u]/=tot;son[u]=tot;vis[u]=0;
}
void dfs_up(int u,int fa){
  vis[u]=1;if(fa) f[u]=1;
  if((son[fa]-1+f[fa])&&fa) up[u]+=(up[fa]*f[fa]+son[fa]*down[fa]-down[u]-dis[u])/(son[fa]-1+f[fa]);
  for(int i=head[u],v;v=to[i],i;i=nxt[i])
    if(!vis[v]) up[v]+=w[i],dfs_up(v,u);
}
void getrt(int rt,int x){
  for(int u=x;u!=fa[rt];u=fa[u])
    cir[++len]=u,b[len+1]=dis[u],vis[u]=1,f[u]=2;
}
void Tarjan(int x,int ff){
  dfn[x]=++tt;fa[x]=ff;
  for(int i=head[x];i;i=nxt[i]){
    int v=to[i];
    if(!dfn[v]) dis[v]=w[i],Tarjan(v,x);
    else if(dfn[v]>dfn[x]) b[1]=w[i],getrt(x,v);
  }
}
void solve_cir(int x,int j,int step){
  double g=0.5,d=0;
  for(int i=1;i<len;i++){
    if(step==-1) d+=b[j];j+=step;
    if(j==0) j=len;if(j==len+1) j=1;
    if(step==1) d+=b[j];
    if(i==len-1) up[x]+=g*(down[cir[j]]+d);
    else up[x]+=g*(down[cir[j]]+d)*son[cir[j]]/(son[cir[j]]+1);
    g/=(son[cir[j]]+1);
  }
}
void work_cir(){
  Tarjan(1,0);
  for(int i=1;i<=len;i++) dfs_down(cir[i]),vis[cir[i]]=1;
  for(int i=1;i<=len;i++) solve_cir(cir[i],i,1),solve_cir(cir[i],i,-1);
  for(int i=1;i<=len;i++) dfs_up(cir[i],0);
}
int main(){
  scanf("%d%d",&n,&m);
  for(int i=1;i<=m;i++){
    int x,y,z;scanf("%d%d%d",&x,&y,&z);lnk(x,y,z);
  }
  if(m==n-1) dfs_down(1),dfs_up(1,0);
  else work_cir();
  for(int i=1;i<=n;i++){
    ans+=(down[i]*son[i]+up[i]*f[i])/(son[i]+f[i]);
  }
  printf("%.5f\n",ans/n);
  return 0;
}

  

bzoj 2878: [Noi2012]迷失遊樂園