题目描述

$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. ucenter 验证码看不到的解决办法

    ucenter 验证码看不到的解决办法,很简单,很实用,本人亲试成功~http://www.jinyuanbao.cn 把images /fonts /en 的ttf 刪除可以了!

  2. 【ABAP系列】SAP LSWM处理时,网络中断,出现错误

    公众号:SAP Technical 本文作者:matinal 原文出处:http://www.cnblogs.com/SAPmatinal/ 原文链接:[ABAP系列]SAP LSWM处理时,网络中断 ...

  3. C++ STL:优先队列的使用详解

    堆是一个很重要的数据结构,那么我们如何更加简洁的去写大根/小根堆呢? 对于很多语言来说,只能一步一步手打,但是对于C++来说,写大根小根堆就简便得多,因为C++中有一个容器叫做priority_que ...

  4. Android深度探索-卷1第五章心得体会

    S3C6410是由三星公司推出的一款低功耗.高性价比的RISC处理器,开发是,首先安装minicom串口调试工具: 第一步:检测当前系统是否支持USB转串口. Lsmod | grep usseria ...

  5. 应用安全-XXE(XML外部实体注入)攻防整理

    libxml2..1及以后,默认不解析外部实体.测试的时候window下使用php5.(libxml Version ), php5.(libxml Version ).Linux中需要将libxml ...

  6. Netty基础-BIO/NIO/AIO

    同步阻塞IO(BIO): 我们熟知的Socket编程就是BIO,每个请求对应一个线程去处理.一个socket连接一个处理线程(这个线程负责这个Socket连接的一系列数据传输操作).阻塞的原因在于:操 ...

  7. BZOJ 1588 (treap)

    题面 传送门 分析 语文题,主要是如何理解最小波动值 设当前天的营业额为x,则最小波动值为min(x-最大的<=x的数,最小的>=x的数-x) 然后用Treap维护序列就可以了 时间复杂度 ...

  8. 图例演示在Linux上快速安装软RAID的详细步骤

    物理环境:虚拟机centos6.4 配置:8G内存.2*2核cpu.3块虚拟硬盘(sda,sdb,sdc,sdb和sdc是完全一样的)        在实际生产环境中,系统硬盘与数据库和应用是分开的, ...

  9. Springboot消除switch-case方法

    Springboot消除switch-case方法 背景 最近,在使用springboot开发一个接口的时候,需要根据接收的请求事件类型,去执行不同的操作,返回不同的结果,基本逻辑如下: String ...

  10. springcloud费话之断路器(hystrix in feign)

    目录: springcloud费话之Eureka基础 springcloud费话之Eureka集群 springcloud费话之Eureka服务访问(restTemplate) springcloud ...