1. 程式人生 > >【xsy1130】tree 樹形dp+期望dp

【xsy1130】tree 樹形dp+期望dp

tex fin 其中 spl png != 節點 必須 def

題目寫得不清不楚的。。。

題目大意:給你一棵$n$個節點的樹,你會隨機選擇其中一個點作為根,隨後隨機每個點深度遍歷其孩子的順序。

下面給你一個點集$S$,問你遍歷完$S$中所有點的期望時間,點集S中的點可能會重復

數據範圍:$n≤10^5$

我們考慮欽定根,然後暴力$dp$。

設$s[u]$表示遍歷以$u$為根的子樹的耗時。

設$f[u]$表示開始遍歷子樹$u$,且最後遍歷在子樹$u$中結束的期望耗時。

不難發現,$s[u]=2\times siz[u]-2$,其中$siz[u]$為以$u$為根的子樹的節點個數。

對於$u$的孩子,我們把它們分成黑點和白點兩類,其中黑點v代表以v為根的子樹內包含有集合$S$中的點,白點代表不包含有集合$S$中的點。

對於任意一種遍歷順序而言,遍歷特征如圖所示:

技術分享圖片

顯然,$b_m$後的節點是不需要遍歷的。

設我們總共有$m$個黑點,則有:

$f[u]=\dfrac{m-1}{m}\sum\limits_{col[v]=black}s[v]+\dfrac{1}{m}\sum\limits_{col[v]=black}(f[v]+1)+\dfrac{m}{m+1}\sum\limits_{col[v]=white}s[v]$

此處的$v$必須滿足是$u$的兒子。

我們通過這個$O(n^2)$的暴力轉移就可以獲得70分的好成績。

考慮滿分做法,我們以$1$為根執行一次$dfs$,求出所有點的f值和s值。

我們進行第二次$dfs$,在$dfs$的過程中維護u的父親的F值。

然後套入剛剛的公式中去求即可。

復雜度就降低到了$O(n)$

技術分享圖片
 1 #include<bits/stdc++.h>
 2 #define M 1005
 3 using namespace std;
 4 
 5 struct edge{int u,next;}e[M*2]={0}; int head[M]={0},use=0;
 6 void add(int x,int y){use++;e[use].u=y;e[use].next=head[x];head[x]=use;}
 7 
 8 int
siz[M]={0},n,S,is[M]={0},ok[M]={0}; 9 double s[M]={0},f[M]={0}; 10 11 void dfs(int x,int fa){ 12 siz[x]=1; ok[x]=is[x]; 13 int m=0; 14 double sumb=0,sumf=0,sumw=0; 15 for(int i=head[x];i;i=e[i].next) if(e[i].u!=fa){ 16 dfs(e[i].u,x); 17 siz[x]+=siz[e[i].u]; 18 ok[x]+=ok[e[i].u]; 19 if(ok[e[i].u]){ 20 m++; 21 sumb+=s[e[i].u]; 22 sumf+=f[e[i].u]+1; 23 }else{ 24 sumw+=s[e[i].u]; 25 } 26 } 27 s[x]=2*siz[x]; 28 if(m){ 29 f[x]=sumb*(m-1)/m+sumf/m+sumw*m/(m+1); 30 } 31 } 32 33 int main(){ 34 scanf("%d",&n); 35 for(int i=1,x,y;i<n;i++) scanf("%d%d",&x,&y),add(x,y),add(y,x); 36 scanf("%d",&S); 37 for(int i=1,x;i<=S;i++) scanf("%d",&x),is[x]=1; 38 double ans=0; 39 for(int i=1;i<=n;i++){ 40 memset(ok,0,sizeof(ok)); 41 memset(siz,0,sizeof(siz)); 42 memset(s,0,sizeof(s)); 43 memset(f,0,sizeof(f)); 44 dfs(i,0); 45 ans+=f[i]; 46 } 47 printf("%.10lf\n",ans/n); 48 }
放一個暴力

這是正解:

 1 #include<bits/stdc++.h>
 2 #define M 100005
 3 #define D double
 4 using namespace std;
 5 
 6 struct edge{int u,next;}e[M*2]={0}; int head[M]={0},use=0;
 7 void add(int x,int y){use++;e[use].u=y;e[use].next=head[x];head[x]=use;}
 8 
 9 int siz[M]={0},n,S,is[M]={0},ok[M]={0};
10 D s[M]={0},f[M]={0},ans=0;
11 
12 void dfs(int x,int fa){
13     siz[x]=1; ok[x]=is[x];
14     int m=0;
15     D sumb=0,sumf=0,sums=0;
16     for(int i=head[x];i;i=e[i].next) if(e[i].u!=fa){
17         dfs(e[i].u,x);
18         siz[x]+=siz[e[i].u];
19         ok[x]+=ok[e[i].u];
20         if(ok[e[i].u]){
21             m++;
22             sumb+=s[e[i].u];
23             sumf+=f[e[i].u]+1;
24         }else{
25             sums+=s[e[i].u];
26         }
27     }
28     s[x]=2*siz[x];
29     if(m){
30         f[x]=sumb*(m-1)/m+sumf/m+sums*m/(m+1);
31     }
32 }
33 void dfs(int x,int fa,D F){
34     int OK=S-ok[x],m=bool(OK);
35     D sumb=0,sumf=0,sums=0;
36     if(m) sumf+=F,sumb+=2*(n-siz[x]); else sums+=2*(n-siz[x]);
37     for(int i=head[x];i;i=e[i].next) if(e[i].u!=fa){
38         if(ok[e[i].u]) m++,sumb+=s[e[i].u],sumf+=f[e[i].u]+1;
39         else sums+=s[e[i].u];
40     }
41     D res=0; if(m) res=sumb*(m-1)/m+sumf/m+sums*m/(m+1);ans+=res;
42     for(int i=head[x];i;i=e[i].next) if(e[i].u!=fa){
43         if(ok[e[i].u]){
44             m--; sumb-=s[e[i].u]; sumf-=f[e[i].u]+1;
45             if(m) F=sumb*(m-1)/m+sumf/m+sums*m/(m+1); else F=0;
46             m++; sumb+=s[e[i].u]; sumf+=f[e[i].u]+1;
47         }else{
48             sums-=s[e[i].u];
49             if(m) F=sumb*(m-1)/m+sumf/m+sums*m/(m+1); else F=0;
50             sums+=s[e[i].u];
51         }
52         dfs(e[i].u,x,F+1);
53     }
54 }
55 
56 int main(){
57     scanf("%d",&n);
58     for(int i=1,x,y;i<n;i++) scanf("%d%d",&x,&y),add(x,y),add(y,x);
59     scanf("%d",&S); int SS=0;
60     for(int i=1,x;i<=S;i++) scanf("%d",&x),SS+=(is[x]==0),is[x]=1;
61     dfs(1,0); S=SS;
62     dfs(1,0,0);
63     printf("%.10lf\n",ans/n);
64 }

【xsy1130】tree 樹形dp+期望dp