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

题目大意:给你一棵$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)$

 #include<bits/stdc++.h>
#define M 1005
using namespace std; struct edge{int u,next;}e[M*]={}; int head[M]={},use=;
void add(int x,int y){use++;e[use].u=y;e[use].next=head[x];head[x]=use;} int siz[M]={},n,S,is[M]={},ok[M]={};
double s[M]={},f[M]={}; void dfs(int x,int fa){
siz[x]=; ok[x]=is[x];
int m=;
double sumb=,sumf=,sumw=;
for(int i=head[x];i;i=e[i].next) if(e[i].u!=fa){
dfs(e[i].u,x);
siz[x]+=siz[e[i].u];
ok[x]+=ok[e[i].u];
if(ok[e[i].u]){
m++;
sumb+=s[e[i].u];
sumf+=f[e[i].u]+;
}else{
sumw+=s[e[i].u];
}
}
s[x]=*siz[x];
if(m){
f[x]=sumb*(m-)/m+sumf/m+sumw*m/(m+);
}
} int main(){
scanf("%d",&n);
for(int i=,x,y;i<n;i++) scanf("%d%d",&x,&y),add(x,y),add(y,x);
scanf("%d",&S);
for(int i=,x;i<=S;i++) scanf("%d",&x),is[x]=;
double ans=;
for(int i=;i<=n;i++){
memset(ok,,sizeof(ok));
memset(siz,,sizeof(siz));
memset(s,,sizeof(s));
memset(f,,sizeof(f));
dfs(i,);
ans+=f[i];
}
printf("%.10lf\n",ans/n);
}

放一个暴力

这是正解:

 #include<bits/stdc++.h>
#define M 100005
#define D double
using namespace std; struct edge{int u,next;}e[M*]={}; int head[M]={},use=;
void add(int x,int y){use++;e[use].u=y;e[use].next=head[x];head[x]=use;} int siz[M]={},n,S,is[M]={},ok[M]={};
D s[M]={},f[M]={},ans=; void dfs(int x,int fa){
siz[x]=; ok[x]=is[x];
int m=;
D sumb=,sumf=,sums=;
for(int i=head[x];i;i=e[i].next) if(e[i].u!=fa){
dfs(e[i].u,x);
siz[x]+=siz[e[i].u];
ok[x]+=ok[e[i].u];
if(ok[e[i].u]){
m++;
sumb+=s[e[i].u];
sumf+=f[e[i].u]+;
}else{
sums+=s[e[i].u];
}
}
s[x]=*siz[x];
if(m){
f[x]=sumb*(m-)/m+sumf/m+sums*m/(m+);
}
}
void dfs(int x,int fa,D F){
int OK=S-ok[x],m=bool(OK);
D sumb=,sumf=,sums=;
if(m) sumf+=F,sumb+=*(n-siz[x]); else sums+=*(n-siz[x]);
for(int i=head[x];i;i=e[i].next) if(e[i].u!=fa){
if(ok[e[i].u]) m++,sumb+=s[e[i].u],sumf+=f[e[i].u]+;
else sums+=s[e[i].u];
}
D res=; if(m) res=sumb*(m-)/m+sumf/m+sums*m/(m+);ans+=res;
for(int i=head[x];i;i=e[i].next) if(e[i].u!=fa){
if(ok[e[i].u]){
m--; sumb-=s[e[i].u]; sumf-=f[e[i].u]+;
if(m) F=sumb*(m-)/m+sumf/m+sums*m/(m+); else F=;
m++; sumb+=s[e[i].u]; sumf+=f[e[i].u]+;
}else{
sums-=s[e[i].u];
if(m) F=sumb*(m-)/m+sumf/m+sums*m/(m+); else F=;
sums+=s[e[i].u];
}
dfs(e[i].u,x,F+);
}
} int main(){
scanf("%d",&n);
for(int i=,x,y;i<n;i++) scanf("%d%d",&x,&y),add(x,y),add(y,x);
scanf("%d",&S); int SS=;
for(int i=,x;i<=S;i++) scanf("%d",&x),SS+=(is[x]==),is[x]=;
dfs(,); S=SS;
dfs(,,);
printf("%.10lf\n",ans/n);
}

【xsy1130】tree 树形dp+期望dp的更多相关文章

  1. [CF697D]Puzzles 树形dp/期望dp

    Problem Puzzles 题目大意 给一棵树,dfs时随机等概率选择走子树,求期望时间戳. Solution 一个非常简单的树形dp?期望dp.推导出来转移式就非常简单了. 在经过分析以后,我们 ...

  2. 概率dp+期望dp 题目列表(一)

    表示对概率和期望还不是很清楚定义. 目前暂时只知道概率正推,期望逆推,然后概率*某个数值=期望. 为什么期望是逆推的,例如你求到某一个点的概率我们可以求得,然后我们只要运用dp从1~n每次都加下去就好 ...

  3. BZOJ1076/Luogu2473 奖励关(SCOI2008)状压DP+期望DP

    题意:给n(n<=15)种宝物宝物有价值w且每个宝物有一个前置宝物(即你必须先吃过它的所有前置宝物至少一次才能吃该宝物),共有m轮游戏,每一轮会在n种宝物等概率选一个出来,因为宝物价值可正可负你 ...

  4. BZOJ2878 [Noi2012]迷失游乐园 【基环树 + 树形dp + 期望dp】

    题目链接 BZOJ2878 题解 除了实现起来比较长,思维难度还是挺小的 观察数据范围发现环长不超过\(20\),而我们去掉环上任何一个点就可以形成森林 于是乎我们枚举断掉的点,然后只需求出剩余每个点 ...

  5. [思路题][LOJ2290][THUWC2017]随机二分图:状压DP+期望DP

    分析 考虑状压DP,令\(f[sta]\)表示已匹配状态是\(sta\)(\(0\)代表已匹配)时完美匹配的期望数量,显然\(f[0]=1\). 一条边出现了不代表它一定在完美匹配内,这也导致很难去直 ...

  6. BZOJ1076: [SCOI2008]奖励关【状压DP+期望DP】

    Description 你正在玩你最喜欢的电子游戏,并且刚刚进入一个奖励关.在这个奖励关里,系统将依次随机抛出k次宝物, 每次你都可以选择吃或者不吃(必须在抛出下一个宝物之前做出选择,且现在决定不吃的 ...

  7. B1076 [SCOI2008]奖励关 状压dp&&期望dp

    这个题的n<15,一看就是状压dp.但是状态不是很好想.f[][]存i关的状态j. 这个题另一个关键思想在于倒推,我一开始想的是正推,但是只能记忆化了. 题干: 题目描述 你正在玩你最喜欢的电子 ...

  8. CF482C Game with Strings (状压DP+期望DP)

    题目大意:甲和乙玩游戏,甲给出n(n<=50)个等长的字符串(len<=20),然后甲选出其中一个字符串,乙随机询问该字符串某一位的字符(不会重复询问一个位置),求乙能确定该串是哪个字符串 ...

  9. Problem Arrangement ZOJ - 3777(状压dp + 期望)

    ZOJ - 3777 就是一个入门状压dp期望 dp[i][j] 当前状态为i,分数为j时的情况数然后看代码 有注释 #include <iostream> #include <cs ...

随机推荐

  1. php-fpm 的 pm.start_servers 参数调整

    大家注意一下 在 php-fpm 的配置文件中, pm.start_servers 必须是介于  pm.min_spare_servers 和  pm.max_spare_servers  这个值之间 ...

  2. [转]图解CSS的padding,margin,border属性(详细介绍及举例说明)

    图解CSS的padding,margin,border属性 W3C组织建议把所有网页上的对像都放在一个盒(box)中,设计师可以通过创建定义来控制这个盒的属性,这些对像包括段落.列表.标题.图片以及层 ...

  3. 创建视图&新建表按照视图结构

    create  view  V_tableTemp as  select a.* from TEMPCLIENT a ,TEMPCLIENTSTUFF b where a.CORNO<>' ...

  4. linux 安装 ORACLE JDK 8

    1.卸载默认的OPENJDK 查看 open jdk 的安装 rpm -qa | grep java 卸载 openjdk rpm -e --nodeps java-1.7.0-openjdk-1.7 ...

  5. telnet 命令使用方法详解,telnet命令怎么用

    telnet 命令使用方法详解,telnet命令怎么用? 文章类型:电脑教程 原创:天诺时空   什么是Telnet? 对于Telnet的认识,不同的人持有不同的观点,可以把Telnet当成一种通信协 ...

  6. Mybatis-Plus 实战完整学习笔记(十一)------条件构造器删除,修改,conditon

    1.修改功能--其他过滤方式跟select一样 /** * 修改条件构造器 * @throws SQLException */ @Test public void selectUpdate() thr ...

  7. javaWeb的验证码编写

    一.前言 验证码可以说在我们生活中已经非常普遍了,任何一个网站,任何一个App都会有这个功能,但是为啥要有这个呢?如何做才能做出来呢?下面小编会带领大家一起用java完成一个验证码的功能. 二.验证码 ...

  8. R语言的文件写入

    R语言的文件写入 官方文档介绍如下: write.table(x, file = "", append = FALSE, quote = TRUE, sep = " &q ...

  9. javascript的Mixins

    mixin在javascript里可以看作是一种从别的对象"借用"功能的方法.每一个新定义的对象都有一个 prototype属性,其他的对象就可以从这里"借用" ...

  10. 20170831工作日记--自定义View学习

    学习了LayoutInflater的原理分析.视图的绘制流程.视图的状态及重绘等知识,按类型来划分的话,自定义View的实现方式大概可以分为三种,自绘控件.组合控件.以及继承控件.那么下面我们就来依次 ...