[CSP-S模拟测试]:联盟(搜索+树的直径)
题目描述
$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模拟测试]:联盟(搜索+树的直径)的更多相关文章
- [CSP-S模拟测试]:Walk(树的直径+数学)
题目描述 给定一棵$n$个节点的树,每条边的长度为$1$,同时有一个权值$w$.定义一条路径的权值为路径上所有边的权值的最大公约数.现在对于任意$i\in [1,n]$,求树上所有长度为$i$的简单路 ...
- [10.12模拟赛] 老大 (二分/树的直径/树形dp)
[10.12模拟赛] 老大 题目描述 因为 OB 今年拿下 4 块金牌,学校赞助扩建劳模办公室为劳模办公室群,为了体现 OI 的特色,办公室群被设计成了树形(n 个点 n − 1 条边的无向连通图), ...
- [CSP-S模拟测试]:count(树分块)
题目描述 李华终于逃离了无尽的英语作文,重获自由的他对一棵树产生了兴趣.首先,他想知道一棵树是否能分成大小相同的几块(即切掉一些边,使得每个连通块的点数相同).然后,他觉得这个问题过于简单,于是他想知 ...
- [07/18NOIP模拟测试5]超级树
鬼能想到的dp定义:dp[i][j]表示在一棵i级超级树中,有j条路径同时存在且这j条路径没有公共点时,可能的情况数 刚开始我也没看懂,所以举个例子 如一个2级的超级树,父节点为1,左右儿子为2,3 ...
- [CSP-S模拟测试]:柱状图(树状数组+二分+三分)
题目描述 $WTH$获得了一个柱状图,这个柱状图一共有$N$个柱子,最开始第$i$根柱子的高度为$x_i$,他现在要将这个柱状图排成一个屋顶的形状,屋顶的定义如下:$1.$屋顶存在一个最高的柱子,假设 ...
- [CSP-S模拟测试]:影魔(树状数组+线段树合并)
题目背景 影魔,奈文摩尔,据说有着一个诗人的灵魂.事实上,他吞噬的诗人灵魂早已成千上万.千百年来,他收集了各式各样的灵魂,包括诗人.牧师.帝王.乞丐.奴隶.罪人,当然,还有英雄.每一个灵魂,都有着自己 ...
- [7.18NOIP模拟测试5]砍树 题解(数论分块)
题面(加密) 又考没学的姿势……不带这么玩的…… 考场上打了个模拟 骗到30分滚粗了 稍加思考(滑稽)可将题面转化为: 求一个最大的$d$,使得 $\sum \limits _{i=1}^n {(\l ...
- [CSP-S模拟测试]:统计(树状数组+乱搞)
题目传送门(内部题120) 输入格式 第一行,两个正整数$n,m$. 第二行,$n$个正整数$a_1,a_2,...,a_n$,保证$1\leqslant a_i\leqslant n$,可能存在相同 ...
- 联赛模拟测试8 Dash Speed 线段树分治
题目描述 分析 对于测试点\(1\).\(2\),直接搜索即可 对于测试点\(3 \sim 6\),树退化成一条链,我们可以将其看成序列上的染色问题,用线段树维护颜色相同的最长序列 对于测试点\(7\ ...
随机推荐
- Memecached 服务器安装(一)
Memecached 服务器安装(一) 前提:首先您的php环境已经安装完成,如若没有则参考 http://www.cnblogs.com/xulele/p/5264781.html 安装环境链接:h ...
- idea中配置Resin运行环境
文章目录 背景 下载resin 配置idea 背景 为了能够读Resin的源码,只看源码看不到值,故想在idea中通过断点查看. 下载resin https://caucho.com/products ...
- php的优势与缺点
PHP即“超文本预处理器”,是一种通用开源脚本语言.PHP是在服务器端执行的脚本语言,与C语言类似,是常用的网站编程语言.PHP独特的语法混合了C.Java.Perl以及 PHP 自创的语法.利于学习 ...
- [Linux] 024 IP 地址配置和网络 yum 源
1. IP 地址配置 (1) 使用 setup 工具 $ setup ps setup 是 RedHat 系列的功能:一般地,Debian系列没有这个功能 Xubuntu 没有这个功能 (2) 启动网 ...
- sql注入判断流程(结合sqli-labs学习)
sql注入判断流程(结合sqli-labs学习) 类型一 类型判断 ?id=1 and 1=2 --+ 如果返回结果正常,说明不是数字类型 and 为两方都为真才算争取 ?id=1' --+ 显示不正 ...
- POJ-1679.The Unique MST.(Prim求次小生成树)
The Unique MST Time Limit: 1000MS Memory Limit: 10000K Total Submissions: 39561 Accepted: 14444 ...
- JS原型链详解
最近面试被问到了就决定好好深入理解原型链 对象 要清楚原型链,首先要弄清楚对象: 普通对象 最普通的对象:有__proto__属性(指向其原型链),没有prototype属性. 原型对象(person ...
- python学习第三十一天函数的嵌套及函数的作用域
python函数的嵌套是指在函数里面嵌套另外一个函数,可以嵌套更多,函数一旦套用了另外一个函数,他的作用域就已经形成,可以通过global关键词改变变量的作用域,下面详细说明函数的嵌套及函数的作用域 ...
- python 字符编码问题总结
都是计算机存储是二进制0101之类的数字 最早计算机在美国开始的 所以数字和英文之类的占用八位 2的8次方 256可以存储对于英文和数字戳戳有余 每个国家都有自己的编码 中国 gb2312 gbk ...
- java中位运算和移位运算详解
一.位运算 (1)按 位 与 & 如果两个相应的二进制形式的对应的位数都为1,则结果为1,记为同1为1,否则为0.首先我们看一下对正数的运算 分别看一下正数和负数的具体运算步骤 ...