树的直径-CF592D Super M
给定一颗n个节点树,边权为1,树上有m个点被标记,问从树上一个点出发,经过所有被标记的点的最短路程(起终点自选)。同时输出可能开始的编号最小的那个点。M<=N<=123456。
先想:如果所有点都被标记那么怎么样?我们发现对于起点s终点t,如果它们在同一条链上,那么必须先从s往外走,再回来,再经过t,再回到t。走过的路径就是树上所有边*2-s到t的路径。如果它们不在同一条链上,那么s在走到t的过程中访问所有点,走过的路径还是树上所有边*2-s到t的路径。
于是如果所有点都被标记,我们应该找到树的直径。如何找树的直径呢?先随便找一个点,然后找从这个点出发能到达的最远点。这个最远点一定是直径的一段,然后再找一次直径即可。(待证)
所以我们只需要把这棵树转成全被标记的就行了。我们随便找一个标记过的点,将其作为树的根,然后只保留标记各个点所在的那条链的上端,因为它们是唯一且一定会经过的边。然后按照算法来就行了。
但这样做还不够,题目还要求输出可能开始的编号最小的那个点。我们发现如果第一次从根开始找,编号最小的点要么是离根最远的点,要么是离根最远的点所找到的最远的点。而离根最远的点能找到的最远的点实际上都是一样的,这就告诉我们直接取最小编号的点,再找一遍,再取最小值就行了。
给出我丑陋的代码:
#include <queue>
#include <cstdio>
#include <vector>
#include <cstring>
using namespace std; const int maxn=;
int n, m, root, printed_num, longest_road;
int printed_list[maxn], printed[maxn], visited[maxn], step[maxn];
vector<int> node[maxn], sons[maxn];
queue<int> q; int make_tree(int pos){
int return_value=, t=, nowson;
visited[pos]=;
for (int i=; i<node[pos].size(); ++i){
nowson=node[pos][i];
if (visited[nowson]) {
sons[pos].push_back(nowson);
continue;
}
t=make_tree(nowson);
if (t) sons[pos].push_back(nowson);
return_value|=t;
}
if (printed[pos]) return_value=;
if (return_value) ++printed_num;
return return_value;
} int main(){
scanf("%d%d", &n, &m);
int t1, t2;
for (int i=; i<n; ++i){
scanf("%d%d", &t1, &t2);
node[t1].push_back(t2);
node[t2].push_back(t1);
}
for (int i=; i<m; ++i){
scanf("%d", &printed_list[i]);
printed[printed_list[i]]=;
}
make_tree(printed_list[]);
if (printed_num==) {
printf("%d\n%d\n", printed_list[], );
return ;
}
root=printed_list[];
memset(visited, , sizeof(visited));
q.push(root);
int nownode, nowson, farthest=, farone=;
while (!q.empty()){
nownode=q.front();
q.pop();
visited[nownode]=;
for (int i=; i<sons[nownode].size(); ++i){
nowson=sons[nownode][i];
if (visited[nowson]) continue;
step[nowson]=step[nownode]+;
q.push(nowson);
}
if (step[nownode]>farthest){
farthest=step[nownode];
farone=nownode;
}
if (step[nownode]==farthest&&farone>nownode)
farone=nownode;
}
int a, b;
a=farone;
farthest=, farone=;
while (!q.empty()) q.pop();
memset(visited, , sizeof(visited));
memset(step, , sizeof(step));
q.push(a);
while (!q.empty()){
nownode=q.front();
q.pop();
visited[nownode]=;
for (int i=; i<sons[nownode].size(); ++i){
nowson=sons[nownode][i];
if (visited[nowson]) continue;
q.push(nowson);
step[nowson]=step[nownode]+;
}
if (step[nownode]>farthest){
farthest=step[nownode];
farone=nownode;
}
if (step[nownode]==farthest&&farone>nownode)
farone=nownode;
}
b=farone;
if (a<b) printf("%d\n", a);
else printf("%d\n", b);
printf("%d", *(printed_num-)-farthest);
return ;
}
树的直径-CF592D Super M的更多相关文章
- Codeforces 592D - Super M - [树的直径][DFS]
Time limit 2000 ms Memory limit 262144 kB Source Codeforces Round #328 (Div. 2) Ari the monster is n ...
- CodeForces - 592D: Super M(虚树+树的直径)
Ari the monster is not an ordinary monster. She is the hidden identity of Super M, the Byteforces’ s ...
- 算法笔记--树的直径 && 树形dp && 虚树 && 树分治 && 树上差分 && 树链剖分
树的直径: 利用了树的直径的一个性质:距某个点最远的叶子节点一定是树的某一条直径的端点. 先从任意一顶点a出发,bfs找到离它最远的一个叶子顶点b,然后再从b出发bfs找到离b最远的顶点c,那么b和c ...
- poj2631 求树的直径裸题
题目链接:http://poj.org/problem?id=2631 题意:给出一棵树的两边结点以及权重,就这条路上的最长路. 思路:求实求树的直径. 这里给出树的直径的证明: 主要是利用了反证法: ...
- poj1985 Cow Marathon (求树的直径)
Cow Marathon Time Limit: 2000MS Memory Limit: 30000K Total Submissions: 3195 Accepted: 1596 Case ...
- VIJOS1476旅游规划[树形DP 树的直径]
描述 W市的交通规划出现了重大问题,市政府下决心在全市的各大交通路口安排交通疏导员来疏导密集的车流.但由于人员不足,W市市长决定只在最需要安排人员的路口安放人员.具体说来,W市的交通网络十分简单,它包 ...
- poj2631 树的直径
设s-t是这棵树的直径,那么对于任意给予的一点,它能够到达的最远的点是s或者t. 这样我们可以通过2次bfs找到树的直径了. #include<cstdio> #include<qu ...
- 【BZOJ-1912】patrol巡逻 树的直径 + DFS(树形DP)
1912: [Apio2010]patrol 巡逻 Time Limit: 4 Sec Memory Limit: 64 MBSubmit: 1034 Solved: 562[Submit][St ...
- 牡丹江.2014B(图论,树的直径)
B - Building Fire Stations Time Limit:5000MS Memory Limit:131072KB 64bit IO Format:%lld & ...
随机推荐
- QuickReport FastReport
一.QuickReport1.安装Component->Install packages->X:/Program Files/Borland/Delphi7/Bin/dclqrt70.bp ...
- 分享知识-快乐自己:shiro 异常类型
<!-- 身份认证异常 --> 1):身份令牌异常,不支持的身份令牌 --> org.apache.shiro.authc.pam.UnsupportedTokenException ...
- Havel-Hakimi定理(握手定理)
Havel-Hakimi定理(握手定理) 由非负整数组成的非增序列s(度序列):d1,d2,…,dn(n>=2,d1>=1)是可图的,当且仅当序列: s1:d2 – 1,d3 – 1,…, ...
- kettle文件以邮件附件的形式发送报告
将从表中导出的excel文件以邮件附件的形式发送报告 step1: 导出文件file1.xls step2: add filename to result将文件添加到结果 step3: 发送邮件
- bzoj 2434: 阿狸的打字机 fail树+离线树状数组
题目大意: http://www.lydsy.com/JudgeOnline/problem.php?id=2434 题解: 首先我们可以发现这个打字的过程本身就是在Trie上滚来滚去的过程 所以我们 ...
- 系列文章--8天学通MongoDB
随笔分类 - MongoDB 8天学通MongoDB——第八天 驱动实践 摘要: 作为系列的最后一篇,得要说说C#驱动对mongodb的操作,目前驱动有两种:官方驱动和samus驱动,不过我个人还是喜 ...
- node-webkit开发基本步骤
详情请查看:http://www.heiboard.com/?p=2091
- Python-IO模式介绍
事件驱动模型:有个事件队列,把事件放到队列里,然后循环这个队列,取出事件执行 5种IO模式: 阻塞 I/O(blocking IO) 非阻塞 I/O(nonblocking IO) I/O 多路复用( ...
- CentOS安装配置radius服务器
1.安装 Yum install -y freeradius freeradius-mysql freeradius-utils 2.配置 1)修改 clients.conf # vi /usr/lo ...
- Java视频播放器的制作
----------------siwuxie095 使用 Java Swing 框架制作一个简单的视频播放器: 首先到 Vid ...