• 【xsy1130】tree 树形dp+期望dp


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

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

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

     数据范围:$n≤10^5$

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

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

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

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

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

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

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

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

    $f[u]=dfrac{m-1}{m}sumlimits_{col[v]=black}s[v]+dfrac{1}{m}sumlimits_{col[v]=black}(f[v]+1)+dfrac{m}{m+1}sumlimits_{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
    ",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
    ",ans/n);
    64 }
  • 相关阅读:
    git修改远程仓库地址
    Result Maps collection already contains value for com.miniprogram.meirong.user.dao.UserMapper.BaseResultMap
    查看微信小程序的appID和secret
    Springboot配置fastjson开发
    本地仓库上传码云
    总结分析Java常见的四种引用
    equals和hashcode 和 ==方法说明
    关于Class.forName(className).newInstance()介绍
    Java枚举
    抽象工厂模式-与-工厂方法模式区别
  • 原文地址:https://www.cnblogs.com/xiefengze1/p/10584320.html
Copyright © 2020-2023  润新知