【xsy1130】tree 树形dp+期望dp

题目写得不清不楚的。。。

题目大意:给你一棵n个节点的树,你会随机选择其中一个点作为根,随后随机每个点深度遍历其孩子的顺序。

下面给你一个点集S,问你遍历完S中所有点的期望时间,点集S中的点可能会重复

 数据范围:n105

我们考虑钦定根,然后暴力dp

s[u]表示遍历以u为根的子树的耗时。

f[u]表示开始遍历子树u,且最后遍历在子树u中结束的期望耗时。

不难发现,s[u]=2×siz[u]2,其中siz[u]为以u为根的子树的节点个数。

对于u的孩子,我们把它们分成黑点和白点两类,其中黑点v代表以v为根的子树内包含有集合S中的点,白点代表不包含有集合S中的点。

对于任意一种遍历顺序而言,遍历特征如图所示:

显然,bm后的节点是不需要遍历的。

设我们总共有m个黑点,则有:

f[u]=m1mcol[v]=blacks[v]+1mcol[v]=black(f[v]+1)+mm+1col[v]=whites[v]

此处的v必须满足是u的儿子。

我们通过这个O(n2)的暴力转移就可以获得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 }
复制代码
posted @   AlphaInf  阅读(237)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 一起来玩mcp_server_sqlite,让AI帮你做增删改查!!
点击右上角即可分享
微信分享提示