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

题目大意:给你一棵$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. Python脚本语言第一行的写法

    脚本语言的第一行,目的就是指出,你想要你的这个文件中的代码用什么可执行程序去运行它,就这么简单 #!/usr/bin/python是告诉操作系统执行这个脚本的时候,调用/usr/bin下的python ...

  2. 2019.01.23 hdu1964 Pipes(轮廓线dp)

    传送门 题意简述:给一个没有障碍的网格图,任意两个格子连通需要花费一定代价,现在求一条覆盖所有格子的哈密顿回路的总权值的最小值. 思路: 跟这道题一毛一样,除了把求和变成求最小值以外. 代码: #in ...

  3. android bug笔记

    昨天集成一个第三方的联盟sdk,然后执行它的代码,程序就会crash,这个第三方的sdk报错日志后面,紧跟着一个友盟分享的报错,我现在也不明白这里是为何把友盟给扯进来了,因为我这步骤操作上并没有调用任 ...

  4. C++STL容器重点

    string 查找和替换 vector 删除

  5. 第31章:MongoDB-索引--复合索引

    ①复合索引 对多个字段创建索引被称为复合索引或者组合索引 ②创建组合索引 创建组合索引(以后台模式创建) db.user.ensureIndex({"username": 1, & ...

  6. DocumentFragment类型

    nodeType 11 nodeName #document-fragment nodeValue NULL parentNode null createdocumentfragment()方法创建了 ...

  7. Ng第十六课:推荐系统(Recommender Systems)

    16.1  问题形式化 16.2  基于内容的推荐系统 16.3  协同过滤 16.4  协同过滤算法 16.5  矢量化:低秩矩阵分解 16.6  推行工作上的细节:均值归一化 16.1  问题形式 ...

  8. bzoj5109(图论好题)

    我的参考题解:https://www.cnblogs.com/ccz181078/p/7907022.html: 不过我感觉题解的压位有问题,(1<<x)还不炸上天.不过这题数据水,好像怎 ...

  9. day24(JAVAWEB上传与下载)

    javaWeb上传与下载 上传: 上传方式: jspSmartUpload   :应用在jsp上的文件上传与下载组件. FileUpload            :用用在jaava环境上的上传的功能 ...

  10. VC中C++数值范围的确定

    1. Visual C++ 32 位和 64 位编译器可识别本文后面的表中的类型. 如果其名称以两个下划线 (__) 开始,则数据类型是非标准的. 下表中指定的范围均包含起始值和结束值. 类型名称 字 ...