Codeforces 804D Expected diameter of a tree(树形DP+期望)
【题目链接】 http://codeforces.com/contest/804/problem/D
【题目大意】
给你一个森林,每次询问给出u,v,
从u所在连通块中随机选出一个点与v所在连通块中随机选出一个点相连,
问你此时相连出的树的直径期望是多少?(如果本身就在同一个连通块内,则输出-1)
【题解】
我们利用树形dp记录每个点属于的连通块,
以及每个点到不同分支最远点的距离,记为mxd[i]
一遍搜索计算出向下最远,再次搜索的时候得到向上最远即可。
得到各个分支的最远距离之后,我们将其进行排序,
通过最远和次远分支计算是否可能成为树的直径,更新所属树的直径的答案diam。
考虑连接u和v所属的树X和树Y,
如果连接是点x和点y,那么所做的期望贡献就是max(mxdx+mxdy+1,diamX,diamY),
考虑枚举每个mxdx和mxdy得到组合来计算期望复杂度过高,我们考虑优化,
我们发现当mxdx+mxdy<max(diamX,diamY)的时候,期望均为max(diamX,diamY)
那么对于mxdx,我们在mxdy的有序集合Disy中分治查找max(diamX,diamY)-mxdx的位置,
就得到对于mxdx来说贡献为max(diamX,diamY)的数量,大于部分则直接求和即可,
我们预处理每个有序集Dis的后缀和,以减少求和的复杂度。
计算出期望之后,考虑到可能有多组点计算的是同一对树,因此我们用map对答案进行记忆化。
【代码】
#include <cstdio>
#include <algorithm>
#include <utility>
#include <map>
#include <vector>
#include <cstring>
using namespace std;
const int N=100010;
typedef long long LL;
vector<int> v[N]; // 邻接表
vector<int> dis[N]; // 该点各个分支的最远距离
int cc[N],cn; // 从属的连通块编号
int mxd[N],diam[N]; // 到树的最远距离和树的直径
vector<int> Dis[N]; // 每个连通块中各点到树的最远距离集
vector<int> sum[N];
int dfs(int x,int fx){
cc[x]=cn;
int Dist=0;
for(int i=0;i<v[x].size();i++){
int y=v[x][i];
if(y==fx)continue;
dis[x][i]=dfs(y,x)+1;
Dist=max(Dist,dis[x][i]);
}return Dist;
}
void dfs2(int x,int fx,int Dis){
for(int i=0;i<v[x].size();i++){
int y=v[x][i];
if(y!=fx)continue;
dis[x][i]=Dis+1;
}if(dis[x].size()==0){mxd[x]=0;diam[cc[x]]=0;return;}
vector<pair<int,int> > Dists(dis[x].size());
for(int i=0;i<Dists.size();i++)Dists[i]=make_pair(dis[x][i],i);
sort(Dists.rbegin(),Dists.rend());
mxd[x]=Dists[0].first;
diam[cc[x]]=max(mxd[x],diam[cc[x]]);
if(Dists.size()>1)diam[cc[x]]=max(diam[cc[x]],Dists[0].first+Dists[1].first);
for(int i=0;i<v[x].size();i++){
int y=v[x][i];
if(y==fx)continue;
if(Dists[0].second!=i)dfs2(y,x,Dists[0].first);
else if(Dists.size()>1)dfs2(y,x,Dists[1].first);
else dfs2(y,x,0);
}
}
LL cal(int x,int y){
if(Dis[x].size()>Dis[y].size())swap(x,y);
LL ans=0;
int mxdiam=max(diam[x],diam[y]);
for(int i=0;i<Dis[x].size();i++){
LL len=lower_bound(Dis[y].begin(),Dis[y].end(),mxdiam-Dis[x][i])-Dis[y].begin();
ans+=len*mxdiam+(Dis[y].size()-len)*(Dis[x][i]+1)+sum[y][len];
}return ans;
}
void init(){
memset(cc,0,sizeof(cc));
memset(v,0,sizeof(v));
memset(dis,0,sizeof(dis));
memset(Dis,0,sizeof(Dis));
memset(sum,0,sizeof(sum));
}
int n,m,q;
int main(){
while(~scanf("%d%d%d",&n,&m,&q)){
init();
for(int i=0;i<m;i++){
int x,y;
scanf("%d%d",&x,&y);
v[x].push_back(y);
v[y].push_back(x);
dis[x].push_back(-1);
dis[y].push_back(-1);
}cn=0;
for(int i=1;i<=n;i++)if(!cc[i]){++cn;dfs(i,-1);dfs2(i,-1,0);}
for(int i=1;i<=n;i++)Dis[cc[i]].push_back(mxd[i]);
for(int i=1;i<=cn;i++){
sort(Dis[i].begin(),Dis[i].end());
sum[i].resize(Dis[i].size()+1);
sum[i][Dis[i].size()]=0;
for(int j=Dis[i].size()-1;j>=0;j--)sum[i][j]=sum[i][j+1]+Dis[i][j];
}
map<pair<int,int>,double> dp;
while(q--){
int x,y;
scanf("%d%d",&x,&y);
x=cc[x],y=cc[y];
if(x==y){puts("-1");continue;}
if(dp.count(make_pair(x,y))==1)printf("%0.9lf\n",dp[make_pair(x,y)]);
else{
LL t=cal(x,y),u=(LL)Dis[x].size()*Dis[y].size();
double ans=(double)t/u;
printf("%0.9lf\n",ans);
dp[make_pair(x,y)]=ans;
}
}
}return 0;
}
Codeforces 804D Expected diameter of a tree(树形DP+期望)的更多相关文章
- Codeforces 804D Expected diameter of a tree
D. Expected diameter of a tree time limit per test 3 seconds memory limit per test 256 megabytes inp ...
- Codeforces 804D Expected diameter of a tree(树的直径 + 二分 + map查询)
题目链接 Expected diameter of a tree 题目意思就是给出一片森林, 若把任意两棵树合并(合并方法为在两个树上各自任选一点然后连一条新的边) 求这棵新的树的树的直径的期望长度. ...
- Codeforces 840D Expected diameter of a tree 分块思想
Expected diameter of a tree 我们先两次dfs计算出每个点能到达最远点的距离. 暴力计算两棵树x, y连边直径的期望很好求, 我们假设SZ(x) < SZ(y) 我们枚 ...
- CodeForces 805F Expected diameter of a tree 期望
题意: 给出一个森林,有若干询问\(u, v\): 从\(u, v\)中所在子树中随机各选一个点连起来,构成一棵新树,求新树直径的期望. 分析: 回顾一下和树的直径有关的东西: 求树的直径 从树的任意 ...
- 【xsy1130】tree 树形dp+期望dp
题目写得不清不楚的... 题目大意:给你一棵$n$个节点的树,你会随机选择其中一个点作为根,随后随机每个点深度遍历其孩子的顺序. 下面给你一个点集$S$,问你遍历完$S$中所有点的期望时间,点集S中的 ...
- CF804D Expected diameter of a tree 树的直径 根号分治
LINK:Expected diameter of a tree 1e5 带根号log 竟然能跑过! 容易想到每次连接两个联通快 快速求出直径 其实是 \(max(D1,D2,f_x+f_y+1)\) ...
- 熟练剖分(tree) 树形DP
熟练剖分(tree) 树形DP 题目描述 题目传送门 分析 我们设\(f[i][j]\)为以\(i\)为根节点的子树中最坏时间复杂度小于等于\(j\)的概率 设\(g[i][j]\)为当前扫到的以\( ...
- [CF697D]Puzzles 树形dp/期望dp
Problem Puzzles 题目大意 给一棵树,dfs时随机等概率选择走子树,求期望时间戳. Solution 一个非常简单的树形dp?期望dp.推导出来转移式就非常简单了. 在经过分析以后,我们 ...
- Codeforces Round #411 (Div. 1) D. Expected diameter of a tree
题目大意:给出一个森林,每次询问给出u,v,问从u所在连通块中随机选出一个点与v所在连通块中随机选出一个点相连,连出的树的直径期望(不是树输出-1).(n,q<=10^5) 解法:预处理出各连通 ...
随机推荐
- 【洛谷 P2756】 飞行员配对方案问题(二分图匹配,最大流)
题目链接 这不是裸的二分图匹配吗? 而且匈牙利算法自带记录方案.. 但既然是网络流24题,那就用网络流来做吧. 具体就是从源点向左边每个点连一条流量为1的边,两边正常连边,流量都是一,右边所有点向汇点 ...
- React的单向数据流与组件间的沟通
今天来给大家总结下React的单向数据流与组件间的沟通. 首先,我认为使用React的最大好处在于:功能组件化,遵守前端可维护的原则. 先介绍单向数据流吧. React单向数据流: React是单向数 ...
- bootstrap-table设置某列序号自增
col = [{ field: 'SerialNumber', title: '序号', formatter: function (value, row, index) { return index+ ...
- Caffe学习笔记3
Caffe学习笔记3 本文为原创作品,未经本人同意,禁止转载,禁止用于商业用途!本人对博客使用拥有最终解释权 欢迎关注我的博客:http://blog.csdn.net/hit2015spring和h ...
- tornado简单使用
这篇适用于快速上手想了解更深:http://www.tornadoweb.cn/ https://tornado-zh.readthedocs.io/zh/latest/ Tornado 是 Fr ...
- FISCO-BCOS平台共识
FISCO-BCOS 应用于区块链的多节点并行拜占庭容错共识算法 看了下微众平台的wiki共识知识 学习下 ()内是自己的思考 参考: https://github.com/FISCO-BCOS/W ...
- 设计模式之笔记--单例模式(Singleton)
单例模式(Singleton) 定义 单例模式(Singleton),保证一个类仅有一个实例,并提供一个访问它的全局访问点. 类图 描述 类Singleton的构造函数的修饰符为private,防止用 ...
- caffe Python API 之卷积层(Convolution)
1.Convolution层: 就是卷积层,是卷积神经网络(CNN)的核心层. 层类型:Convolution lr_mult: 学习率的系数,最终的学习率是这个数乘以solver.prototxt配 ...
- PHP获取ip与ip所在城市
1获取真实ip,本地测试总是::1 或者127.0.0.1 或者局域网的ip /** * 获取用户真实 IP */ function getIP() { static $realip; if (iss ...
- Java中的原子操作类
转载: <ava并发编程的艺术>第7章 当程序更新一个变量时,如果多线程同时更新这个变量,可能得到期望之外的值,比如变量i=1,A线程更新i+1,B线程也更新i+1,经过两个线程操作之后可 ...