题目描述

$G$国周边的$n$个小国家构成一个联盟以抵御$G$国入侵,为互相支援,他们建立了$n−1$条双向通路,使得任意两个国家可以经过通路相互到达。
当一个国家受到攻击时,所有其它国家都会沿着最短路径前往这个国家进行支援,经过每条通路所需的时间均为$1$。定义一个国家的危险程度为所有国家全部赶到需要的最短时间,联盟的危险程度为所有国家的危险程度的最大值。
为了降低危险程度,联盟决定断开一条通路并任意连接一条通路,使得危险程度尽可能小,并要求改建完成之后任意两个国家可以经过通路互相到达。他们决定让你来设计方案,你需要告知在最优方案中可能断开哪些边,并给出任意一组最优方案。


输出格式

第一行一个正整数$n$。
接下来$n−1$行每行两个正整数,表示一条$u_i,v_i$之间的边。


输出格式

输出第一行一个整数表示最小危险程度。
第二行一个整数$k$,表示可能被断开的边的数量,接下来$k$个数,表示可能断开的边的编号,按升序输出。
接下来一行四个正整数表示一组最优方案,分别表示断开和新建的边的端点,只需给出任意一组合法的方案即可。


样例

样例输入:

4
1 2
2 3
3 4

样例输出:

2
2 1 3
3 4 4 2


数据范围与提示

对于$20\%$的数据,$n\leqslant 30$。
对于$40\%$的数据,$n\leqslant 300$。
对于$60\%$的数据,$n\leqslant 3,000$。
对于$100\%$的数据,$n\leqslant 300,000$。

如果你的答案仅第一行正确,你可以获得$25\%$的分数,
如果你的答案仅前两行正确,你可以获得$50\%$的分数,
为保证得到部分分请确保提交程序的输出格式符合题目要求。


题解

联盟的危险程度其实就是树的直径,这个我们使用两遍$DFS$即可解决。

然后我们还可以用两遍$DFS$求出每棵子树的直径。

现在我们枚举断边,至于如何连边,显然我们是要将两个联通块中直径的中点相连一定最优。

这时候我们已经知道第一问和第二问了。

第三问在跑$DFS$就解决了。

时间复杂度:$\Theta(n)$。

期望得分:$100$分。

实际得分:$100$分。


代码时刻

#include<bits/stdc++.h>
using namespace std;
struct rec{int nxt,to;}e[700000];
int head[300001],cnt=1;
int n;
int len,st,ed,ban;
int dis[2][300001];
int dfn[300001],low[300001];
int dp[2][300001];
int ans[300001],sum;
void add(int x,int y)
{
e[++cnt].nxt=head[x];
e[cnt].to=y;
head[x]=cnt;
}
void dfs1(int x,int fa,int tim)
{
if(tim>=len)
{
len=tim;
st=x;
}
for(int i=head[x];i;i=e[i].nxt)
if(e[i].to!=fa&&(ban!=i)&&((ban^1)!=i))dfs1(e[i].to,x,tim+1);
}
void dfs2(int x,int fa,int tim)
{
if(tim>=len)
{
len=tim;
ed=x;
}
for(int i=head[x];i;i=e[i].nxt)
if(e[i].to!=fa&&(ban!=i)&&((ban^1)!=i))
{
dfs2(e[i].to,x,tim+1);
dis[1][i>>1]=e[i].to;
}
}
bool dfs3(int x,int fa)
{
dfn[++dfn[0]]=x;
if(x==ed)return 1;
for(int i=head[x];i;i=e[i].nxt)
if(e[i].to!=fa&&(ban!=i)&&((ban^1)!=i)&&dfs3(e[i].to,x))
{
low[i>>1]=1;
return 1;
}
dfn[0]--;
return 0;
}
int dfs4(int x,int fa)
{
int maxn=0;
for(int i=head[x];i;i=e[i].nxt)
if(e[i].to!=fa)
{
int flag=dfs4(e[i].to,x);
dp[0][x]=max(dp[0][x],dp[0][e[i].to]);
dp[0][x]=max(dp[0][x],flag+maxn);
maxn=max(maxn,flag);
}
return maxn+1;
}
int dfs5(int x,int fa)
{
int maxn=0;
for(int i=head[x];i;i=e[i].nxt)
if(e[i].to!=fa)
{
int flag=dfs5(e[i].to,x);
dp[1][x]=max(dp[1][x],dp[1][e[i].to]);
dp[1][x]=max(dp[1][x],flag+maxn);
maxn=max(maxn,flag);
}
return maxn+1;
}
int main()
{
ans[0]=1<<30;
scanf("%d",&n);
for(int i=1;i<n;i++)
{
int x,y;
scanf("%d%d",&x,&y);
add(x,y);add(y,x);
}
dfs1(1,0,0);
len=0;
dfs2(st,0,0);
dfs3(st,0);
dfs4(st,0);
dfs5(ed,0);
for(int i=1;i<n;i++)
{
if(e[i<<1].to==dis[1][i])dis[0][i]=e[i<<1|1].to;
else dis[0][i]=e[i<<1].to;
}
for(int i=1;i<n;i++)
if(!low[i])
{
ans[i]=max((len+1)/2+min(dp[0][e[i<<1].to]+1,dp[0][e[i<<1|1].to]+1)/2+1,len);
ans[0]=min(ans[0],ans[i]);
}
else
{
ans[i]=max(max(dp[0][dis[1][i]],dp[1][dis[0][i]]),(dp[0][dis[1][i]]+1)/2+(dp[1][dis[0][i]]+1)/2+1);
ans[0]=min(ans[0],ans[i]);
}
printf("%d\n",ans[0]);
for(int i=1;i<n;i++)if(ans[i]==ans[0])sum++;
printf("%d ",sum);
for(int i=1;i<n;i++)if(ans[i]==ans[0])printf("%d ",i);
puts("");
for(int i=1;i<n;i++)
if(ans[i]==ans[0])
{
printf("%d %d ",e[i<<1].to,e[i<<1|1].to);
ban=i<<1;
len=dfn[0]=0;
dfs1(e[ban].to,0,0);
len=0;
dfs2(st,0,0);
dfs3(st,0);
printf("%d ",dfn[dfn[0]+1>>1]);
len=dfn[0]=0;
dfs1(e[ban^1].to,0,0);
len=0;
dfs2(st,0,0);
dfs3(st,0);
printf("%d ",dfn[dfn[0]+1>>1]);
break;
}
return 0;
}

rp++

[CSP-S模拟测试]:联盟(搜索+树的直径)的更多相关文章

  1. [CSP-S模拟测试]:Walk(树的直径+数学)

    题目描述 给定一棵$n$个节点的树,每条边的长度为$1$,同时有一个权值$w$.定义一条路径的权值为路径上所有边的权值的最大公约数.现在对于任意$i\in [1,n]$,求树上所有长度为$i$的简单路 ...

  2. [10.12模拟赛] 老大 (二分/树的直径/树形dp)

    [10.12模拟赛] 老大 题目描述 因为 OB 今年拿下 4 块金牌,学校赞助扩建劳模办公室为劳模办公室群,为了体现 OI 的特色,办公室群被设计成了树形(n 个点 n − 1 条边的无向连通图), ...

  3. [CSP-S模拟测试]:count(树分块)

    题目描述 李华终于逃离了无尽的英语作文,重获自由的他对一棵树产生了兴趣.首先,他想知道一棵树是否能分成大小相同的几块(即切掉一些边,使得每个连通块的点数相同).然后,他觉得这个问题过于简单,于是他想知 ...

  4. [07/18NOIP模拟测试5]超级树

    鬼能想到的dp定义:dp[i][j]表示在一棵i级超级树中,有j条路径同时存在且这j条路径没有公共点时,可能的情况数 刚开始我也没看懂,所以举个例子 如一个2级的超级树,父节点为1,左右儿子为2,3 ...

  5. [CSP-S模拟测试]:柱状图(树状数组+二分+三分)

    题目描述 $WTH$获得了一个柱状图,这个柱状图一共有$N$个柱子,最开始第$i$根柱子的高度为$x_i$,他现在要将这个柱状图排成一个屋顶的形状,屋顶的定义如下:$1.$屋顶存在一个最高的柱子,假设 ...

  6. [CSP-S模拟测试]:影魔(树状数组+线段树合并)

    题目背景 影魔,奈文摩尔,据说有着一个诗人的灵魂.事实上,他吞噬的诗人灵魂早已成千上万.千百年来,他收集了各式各样的灵魂,包括诗人.牧师.帝王.乞丐.奴隶.罪人,当然,还有英雄.每一个灵魂,都有着自己 ...

  7. [7.18NOIP模拟测试5]砍树 题解(数论分块)

    题面(加密) 又考没学的姿势……不带这么玩的…… 考场上打了个模拟 骗到30分滚粗了 稍加思考(滑稽)可将题面转化为: 求一个最大的$d$,使得 $\sum \limits _{i=1}^n {(\l ...

  8. [CSP-S模拟测试]:统计(树状数组+乱搞)

    题目传送门(内部题120) 输入格式 第一行,两个正整数$n,m$. 第二行,$n$个正整数$a_1,a_2,...,a_n$,保证$1\leqslant a_i\leqslant n$,可能存在相同 ...

  9. 联赛模拟测试8 Dash Speed 线段树分治

    题目描述 分析 对于测试点\(1\).\(2\),直接搜索即可 对于测试点\(3 \sim 6\),树退化成一条链,我们可以将其看成序列上的染色问题,用线段树维护颜色相同的最长序列 对于测试点\(7\ ...

随机推荐

  1. 【转】PyQt5系列教程(七)控件

    PyQt5系列教程(七)控件   软硬件环境 Windows 10 Python 3.4.2 PyQt 5.5.1 PyCharm 5.0.4 前言 控件是PyQt应用程序的基石.PyQt5自带很多不 ...

  2. 本站页脚HTML回顶部代码

    <style type="text/css">.top { width: 50px; height: 50px; background-color: #F0F0F0; ...

  3. Vagrant 入门 - share

    原文地址 译者注:Vagrant Share 功能通过 ngrok 向所有人提供访问内网开发环境的能力. 现在我们已经启动并运行了一台 Web 服务器,并且可以从你的机器访问,我们拥有一个相当实用的开 ...

  4. 2019/10/27 TZOJ

    1001 Gaussian Prime http://www.tzcoder.cn/acmhome/problemdetail.do?&method=showdetail&id=379 ...

  5. PHP_CodeIgniter _remap重新定义方法

    如果controller定义了_remap方法, 在_remap中重新定义方法 class Test extends CI_Controller{ public function index(){ e ...

  6. Uncaught TypeError: Cannot set property onclick' of null

    如果出现以上问题,只需要把<script src="xxx.js"></script> 移动到最后,</body>的前面;

  7. Angular2+之模态框-使用ngx-bootstrap包中的模态框组件实现

    模态框是项目中经常会用到的一个公共功能,通常会被用左提示框或者扩展选项框. 下面,我用一个小例子来简单展示实现模态框功能的过程: 1.为项目加包: ng add ngx-bootstrap 2.在xx ...

  8. Cannot modify header information - headers already sent by出错的原因

    <?php ob_start(); setcookie("username","送家",time()+3600); echo "the user ...

  9. Python自学第二天学习之《列表》

    一.  列表:list类型,是有序的,可以被修改的. 格式 : li=["cd",1,"gfds",[1,2,3]] 1.类型转换: #字符串转换成列表 b=“ ...

  10. web前端之html基础知识初级

    html 基础标签 单标签 1.注释标签: ctrl+/ 换行标签: 横线标签: 标题标签: 段落标签: 表示强调标签: 文字 属性:文字加颜色 color:改变文字颜色 size:改文字大小属性 例 ...