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

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

    definition  a device that selects one of several analog or digital input signals and forwards the se ...

  2. c#用EPPLUS操作excel

    参考: http://www.cnblogs.com/rumeng/p/3785748.html http://www.cnblogs.com/libla/p/5824296.html#3818995 ...

  3. 第19章:MongoDB-聚合操作--聚合管道--$limit+$skip

    ①$limit “$limit”:负责数据的取出个数: “$skip”:数据的跨过个数. ②范例:使用“$limit”设置取出的个数

  4. 第08章:MongoDB-CRUD操作--文档--删除

    ①语法 remove()  [2.6以后方法过时] deleteOne() [2.6以后官方推荐] deleteMany() [2.6以后官方推荐] db.collection.remove( < ...

  5. hadoop sqoop的常用名命令

    1 列出所有的ambari数据库中所有的表 Sqoop list-tables -connect  jdbc:mysql://localhost:3306/ambari -username ambar ...

  6. nginx 下载 大文件被截断

    如果出现大文件被截断,且ngix的日志大量出现以下类似报错: 则说明是nginx没有fastcgi_temp的读写权限.其中fastcgi_temp是自己的文件夹名称,每个人的不同且路径也会不同,这个 ...

  7. 1.2.2实现Runnable接口

    使用Runnable创建线程 package com.cky.runner; /** * Created by chenkaiyang on 2017/12/2. */ public class My ...

  8. rpcbind.service启动失败

    新装的服务器,启动rpcbind.service通常失败,执行下面的两个命令经常卡死,一直不返回,也不报错 #systemctl start nfs-server.service #systemctl ...

  9. Java并发编程:线程池的使用(转载)

    文章出处:http://www.cnblogs.com/dolphin0520/p/3932921.html Java并发编程:线程池的使用 在前面的文章中,我们使用线程的时候就去创建一个线程,这样实 ...

  10. java中大数的一些基本运算

    import java.math.BigInteger; import java.util.Scanner; public class Main { public static void main(S ...