给定一颗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的更多相关文章

  1. Codeforces 592D - Super M - [树的直径][DFS]

    Time limit 2000 ms Memory limit 262144 kB Source Codeforces Round #328 (Div. 2) Ari the monster is n ...

  2. CodeForces - 592D: Super M(虚树+树的直径)

    Ari the monster is not an ordinary monster. She is the hidden identity of Super M, the Byteforces’ s ...

  3. 算法笔记--树的直径 && 树形dp && 虚树 && 树分治 && 树上差分 && 树链剖分

    树的直径: 利用了树的直径的一个性质:距某个点最远的叶子节点一定是树的某一条直径的端点. 先从任意一顶点a出发,bfs找到离它最远的一个叶子顶点b,然后再从b出发bfs找到离b最远的顶点c,那么b和c ...

  4. poj2631 求树的直径裸题

    题目链接:http://poj.org/problem?id=2631 题意:给出一棵树的两边结点以及权重,就这条路上的最长路. 思路:求实求树的直径. 这里给出树的直径的证明: 主要是利用了反证法: ...

  5. poj1985 Cow Marathon (求树的直径)

    Cow Marathon Time Limit: 2000MS   Memory Limit: 30000K Total Submissions: 3195   Accepted: 1596 Case ...

  6. VIJOS1476旅游规划[树形DP 树的直径]

    描述 W市的交通规划出现了重大问题,市政府下决心在全市的各大交通路口安排交通疏导员来疏导密集的车流.但由于人员不足,W市市长决定只在最需要安排人员的路口安放人员.具体说来,W市的交通网络十分简单,它包 ...

  7. poj2631 树的直径

    设s-t是这棵树的直径,那么对于任意给予的一点,它能够到达的最远的点是s或者t. 这样我们可以通过2次bfs找到树的直径了. #include<cstdio> #include<qu ...

  8. 【BZOJ-1912】patrol巡逻 树的直径 + DFS(树形DP)

    1912: [Apio2010]patrol 巡逻 Time Limit: 4 Sec  Memory Limit: 64 MBSubmit: 1034  Solved: 562[Submit][St ...

  9. 牡丹江.2014B(图论,树的直径)

    B - Building Fire Stations Time Limit:5000MS     Memory Limit:131072KB     64bit IO Format:%lld & ...

随机推荐

  1. 通知消息与ON_NOTIFY

    1.通知消息一般是由子控件发出,由父窗口响应,因此响应函数的位置在父窗口内. 2.通知消息发送给父窗口的是通知码,即WM_NOTIFY消息(但为了区分方便不同的消息有不同的名称,但都是以WM_NOTI ...

  2. hihocoder-1285 智力竞赛(区间dp)

    智力竞赛 时间限制:5000ms 单点时限:1000ms 内存限制:256MB 描述 小Hi.小Ho还有被小Hi强拉来的小Z,准备组队参加一个智力竞赛.竞赛采用过关制,共计N个关卡.在第i个关卡中,小 ...

  3. vscode 小笔记

    用户设置: { "git.ignoreMissingGitWarning": true, "workbench.statusBar.feedback.visible&qu ...

  4. java正则表达式匹配文本中想要的字符串

    需求:获取一个本地文件中所有符合 $[MAKE_PACKAGE] 格式的字符串,并输出到另一个文件中. public static void main(String[] args) throws Ex ...

  5. linux命令学习笔记(35):ln 命令

    ln是linux中又一个非常重要命令,它的功能是为某一个文件在另外一个位置建立一个同步的链接.当我们需要在 不同的目录,用到相同的文件时,我们不需要在每一个需要的目录下都放一个必须相同的文件,我们只要 ...

  6. 点分治Day1

    树套树Day2暂且搁置...因为Day1的题我各种不会做... 唯一过了一道还是整体二分过的... 我们来一点愉快的算法,先不考虑数据结构这种骚东西了 毕竟还在发烧,就先码码这几天在搞的点分治吧 hx ...

  7. 线段树Final版本

    结构体是个好东西... 看着逼格很高 #include<iostream> #include<cstdio> #include<cstdlib> #include& ...

  8. ngget配置

    Install-Package NuGet.CommandLine nuget spec nuget pack Jryg.VirtualNumber.ClientNet4.csproj -Includ ...

  9. 51nod 1686 第K大区间2

    1685 第K大区间2 定义一个区间的值为其众数出现的次数.现给出n个数,求将所有区间的值排序后,第K大的值为多少. 众数(统计学/数学名词)_百度百科 Input 第一行两个数n和k(1<=n ...

  10. Brunch with a Friend 与朋友共进午餐

    brownies 核仁巧克力饼 toast 烤面包 dining room 餐厅 practical 实用的 meal 一餐 combination 组合 pancake 薄煎饼 waffle 华夫饼 ...