樹形+基環樹DP/數學期望
然而我並不會做……
題解戳這里:http://blog.csdn.net/u011265346/article/details/46328543
好吧先考慮一個簡單點的,當m=n-1時,整個是一個樹形的結構,無根樹我們一般還是轉成有根樹來處理……然后既然是無法回頭的,那么我們可以定一下方向:向下或者向上(廢話)
定義一下:
son[x]為x的兒子的數量
down[x]表示從x這個點出發,向葉子們走的期望長度。
怎么算呢?其實就是所有可能的情況(所有的兒子)加起來,再求個平均,所以我們有$$down[x]=\frac{\sum (down[y]+len(x->y))}{son[x]}$$
up[x]表示從x這個點出發,經過父親,走到某個葉子的期望長度。
這個看上去好難算啊……怎么算呢?我們先算出來每個結點的down[x],然后從上往下DP,這里我們需要考慮的是x的父親 f ,我們從x向上走到他父親 f 后(len(x->f)),可以向 f 其他的兒子走,這種方案的期望長度之和為$$ son[f]*down[f]-down[x]-len(f->x) $$如果是繼續向上,這種方案的期望長度為$up[f]$,所以有$$up[x]=len(x->f) + \frac{ son[f]*down[f]-down[x]-len(f->x) + up[f]}{son[f]-1+1}$$這里son[f]-1表示是向其他兒子走,+1表示的是繼續向上,這么多種方案的概率是相等的。
那么答案怎么算?當然是$$ans=\sum_{i=1}^n \frac{down[i]*son[i]+up[i]}{son[i]+1} $$
好的到這里我們就解決了樹上的問題,那么環套樹其實就是需要特殊處理一下 環上的結點以及與環直接相連的結點,怎么做呢?
肯定是要先找環的啦= =那么我們dfs搞搞找出環上所有結點,題目限制這樣的結點不多,就20個= =
有一個東西是跟樹的情況一樣的,那就是從環上某個結點直接向與它相連的外向樹上走的期望長度,也就是所有的down[cir[i]],這里cir[i]表示環上的點。
算出down[cir[i]]以后,跟據剛剛的經驗,我們就可以用down來算up啦!同樣是從上往下算up的值,只不過這里的“樹根”變成了一個環,環上的點的up值其實就是沿着環走到其他的任意一個環上的點,然后再向下走的期望長度啦,舉個例子吧:(其實這一段看代碼比較好……)
環上的點編號為1、2、3、4、5,那么對於1來說,順時針走的話,走到2的概率為1,走到3的概率為$\frac{1}{son[2]+1}$,走到4的話就再乘$\frac{1}{son[3]+1}$……逆時針走的話同理。
同時我們沿着環每走到一個位置就加上從這里向外向樹走的期望長度(注意繞一圈走到頭的地方與之前的不一樣,因為出發點不可能經過兩次)。
表達式比較難寫……好吧其實是我懶,而且長得並不好看,還是看代碼比較好:

1 F(i,1,tot){ 2 int x=cir[i]; 3 double k=1; 4 for(int j=nex[x];j!=x;j=nex[j]){ 5 if (nex[j]!=x) 6 up[x]+=k*(len[hash[pre[j]]][hash[j]]+down[j]*son[j]/(son[j]+1)); 7 else 8 up[x]+=k*(len[hash[pre[j]]][hash[j]]+down[j]); 9 k/=(son[j]+1); 10 } 11 k=1; 12 for(int j=pre[x];j!=x;j=pre[j]){ 13 if (pre[j]!=x) 14 up[x]+=k*(len[hash[nex[j]]][hash[j]]+down[j]*son[j]/(son[j]+1)); 15 else 16 up[x]+=k*(len[hash[nex[j]]][hash[j]]+down[j]); 17 k/=(son[j]+1); 18 } 19 up[x]/=2; 20 }
算出所有環上的點的up[i]以后,外向樹的up[i]就跟樹上情況一樣了……不過由於環上的點的父節點相當於是有兩個,所以這里重新定義一個fa[i],表示 i 的父親結點的數量。那么有$$up[x]=len(x->f) + \frac{ son[f]*down[f]-down[x]-len(f->x) + up[f]*fa[f] }{son[f]-1+fa[f]}$$
另外,對於上面的所有公式,要小心分母可能為0的情況。

1 /************************************************************** 2 Problem: 2878 3 User: Tunix 4 Language: C++ 5 Result: Accepted 6 Time:556 ms 7 Memory:9876 kb 8 ****************************************************************/ 9 10 //BZOJ 2878 11 #include<vector> 12 #include<cstdio> 13 #include<cstring> 14 #include<cstdlib> 15 #include<iostream> 16 #include<algorithm> 17 #define rep(i,n) for(int i=0;i<n;++i) 18 #define F(i,j,n) for(int i=j;i<=n;++i) 19 #define D(i,j,n) for(int i=j;i>=n;--i) 20 #define pb push_back 21 using namespace std; 22 inline int getint(){ 23 int v=0,sign=1; char ch=getchar(); 24 while(ch<'0'||ch>'9'){ if (ch=='-') sign=-1; ch=getchar();} 25 while(ch>='0'&&ch<='9'){ v=v*10+ch-'0'; ch=getchar();} 26 return v*sign; 27 } 28 const int N=1e5+10,INF=~0u>>2; 29 typedef long long LL; 30 /******************tamplate*********************/ 31 int to[N<<1],nxt[N<<1],head[N],cnt; 32 double l[N<<1]; 33 void ins(int x,int y,double z){ 34 to[++cnt]=y; nxt[cnt]=head[x]; head[x]=cnt; l[cnt]=z; 35 } 36 37 int n,m; 38 39 int vis[N],flag; 40 double son[N],fa[N],up[N],down[N]; 41 int cir[N],tot,hash[N]; 42 int pre[N],nex[N]; 43 double len[25][25]; 44 45 void Findcir(int x,int f){ 46 vis[x]=1; 47 for(int i=head[x];i;i=nxt[i]) 48 if (to[i]!=f){ 49 if (vis[to[i]]){ 50 flag=to[i]; 51 return; 52 } 53 Findcir(to[i],x); 54 if (flag>0){ 55 if (flag==x) flag=-1; 56 return; 57 } 58 if (flag==-1) break; 59 } 60 vis[x]=0; 61 } 62 void dfs_cir(int x,int f){ 63 if (hash[x]) return; 64 cir[++tot]=x; 65 hash[x]=tot; 66 fa[x]=2; 67 for(int i=head[x];i;i=nxt[i]){ 68 if (to[i]==f) continue; 69 if (!vis[to[i]]) continue; 70 71 pre[to[i]]=x; 72 nex[x]=to[i]; 73 dfs_cir(to[i],x); 74 len[hash[x]][hash[to[i]]]=len[hash[to[i]]][hash[x]]=l[i]; 75 break; 76 } 77 } 78 79 void dfsdown(int x,int f){ 80 for(int i=head[x];i;i=nxt[i]) 81 if (!vis[to[i]] && to[i]!=f){ 82 fa[to[i]]=1; 83 dfsdown(to[i],x); 84 son[x]++; 85 down[x]+=down[to[i]]+l[i]; 86 } 87 if (son[x]) down[x]/=son[x]; 88 } 89 90 void dfsup(int x,int f,double ee){ 91 up[x]=ee; 92 if (fa[f]+son[f]>1) 93 up[x]+=(fa[f]*up[f]+son[f]*down[f]-down[x]-ee)/(fa[f]+son[f]-1); 94 for(int i=head[x];i;i=nxt[i]) 95 if (to[i]!=f) dfsup(to[i],x,l[i]); 96 } 97 98 int main(){ 99 #ifndef ONLINE_JUDGE 100 freopen("2878.in","r",stdin); 101 freopen("2878.out","w",stdout); 102 #endif 103 n=getint(); m=getint(); 104 F(i,1,m){ 105 int x=getint(),y=getint(),z=getint(); 106 ins(x,y,z); ins(y,x,z); 107 } 108 Findcir(1,0); 109 if (m<n){ 110 dfsdown(1,0); 111 for(int i=head[1];i;i=nxt[i]) 112 dfsup(to[i],1,l[i]); 113 }else{ 114 F(i,1,n) 115 if (vis[i]){ 116 dfs_cir(i,0); 117 break; 118 } 119 F(i,1,tot) dfsdown(cir[i],0); 120 F(i,1,tot){ 121 int x=cir[i]; 122 double k=1; 123 for(int j=nex[x];j!=x;j=nex[j]){ 124 if (nex[j]!=x) 125 up[x]+=k*(len[hash[pre[j]]][hash[j]]+down[j]*son[j]/(son[j]+1)); 126 else 127 up[x]+=k*(len[hash[pre[j]]][hash[j]]+down[j]); 128 k/=(son[j]+1); 129 } 130 k=1; 131 for(int j=pre[x];j!=x;j=pre[j]){ 132 if (pre[j]!=x) 133 up[x]+=k*(len[hash[nex[j]]][hash[j]]+down[j]*son[j]/(son[j]+1)); 134 else 135 up[x]+=k*(len[hash[nex[j]]][hash[j]]+down[j]); 136 k/=(son[j]+1); 137 138 } 139 up[x]/=2; 140 } 141 F(j,1,tot){ 142 for(int i=head[cir[j]];i;i=nxt[i]) 143 if (!hash[to[i]]) dfsup(to[i],cir[j],l[i]); 144 } 145 } 146 double ans=0; 147 F(i,1,n) ans+=(up[i]*fa[i]+down[i]*son[i])/(fa[i]+son[i]); 148 printf("%.5lf\n",ans/n); 149 return 0; 150 }
2878: [Noi2012]迷失游樂園
Time Limit: 10 Sec Memory Limit: 512 MBSec Special JudgeSubmit: 503 Solved: 321
[Submit][Status][Discuss]
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
1 2 3
2 3 1
3 4 4
Sample Output
【樣例解釋】樣例數據中共有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