洛谷 P3629 [APIO2010]巡逻
题目在这里
这是一个紫题,当然很难。
我们往简单的想,不建立新的道路时,从1号节点出发,把整棵树上的每条边遍历至少一次,再回到1号节点,会恰好经过每条边两次,路线总长度为$2(n-1)$,根据树的深度优先遍历思想,很容易证明这个结论,因为每条边必然被递归一次,回溯一次。
建立1条新道路之后,因为新道路必须恰好经过一次(0次,2次都不可以),所以在沿着新道路(x,y)巡逻之后,要返回x,就必须沿着树上从y到x的路径巡逻一遍,最终形成一个环。与不建立新道路的情况相结合,相当于树上x与y之间的路径就只需经过一次了。
因此,当$k=1$时,我们找到树的最长链,在两个端点之间加一条新道路,就能让总的巡逻距离最小。若树的直径为L,答案就是$2(n-1)-L+1$。
建立第2条新道路(u,v)之后,又会形成一个环。若两条新道路形成的环不重叠,则树上u,v之间的路径只需经过一次,答案继续减小。否则,在两个环重叠的情况下,如果我们还按照刚才的方法把第2个环与建立1条新道路的情况相结合,两个环重叠的部分就不会被巡逻到。最终的结果是两个环重叠的部分由只需经过一次变回了需要经过两次。
综上所述,我们得到了如下算法:
1.在最初的树上求直径,设直径为$L1$。然后,把直径上的边权取反(从1改为-1)。
2.在最长链边权取反之后的树上再次求直径,设直径为$L2$。
答案就是$2(n-1)-(L1-1)-(L2-1)=2n-L1-L2$。如果L2这条直径包含L1取反的部分,就相当于两个环重叠。减掉$(L1-1)$后,重叠的部分变成了只需经过一次,减掉$(L2-1)$后,相当于把重叠的部分加回来,变回需要经过两次,与我们之前讨论相符。时间复杂度为$O(n)$。
code:
// luogu-judger-enable-o2
#include <bits/stdc++.h>
using namespace std; queue <int> q;
const int N=;
bool v[N];
int f[N];
int n,k,e=,cnt,p;
int s[N<<][],o[N],d[N],fa[N],w[N<<]; void add(int x,int y)
{
s[++e][]=y;s[e][]=o[x];o[x]=e;w[e]=;
} int bfs(int xx)
{
int ans=xx;
memset(v,,sizeof(v));
fa[xx]=;v[xx]=;d[xx]=;q.push(xx);
while (!q.empty()) {
int x=q.front();
for (int i=o[x];i;i=s[i][]) {
int y=s[i][];
if (!v[y]) {
fa[y]=x;d[y]=d[x]+;
if (d[y]>d[ans]) ans=y;
v[y]=;q.push(y);
}
}
q.pop();
}
return ans;
} void mark(int x)
{
int i=o[x];
while (i) {
int y=s[i][];
if (y!=fa[x]) {
if (v[x]&&v[y]) w[i]=w[i^]=-;
mark(y);
}
i=s[i][];
}
} void tree_dp(int x)
{
int mx=,i=o[x];
while (i) {
int y=s[i][];
if (y!=fa[x]) {
tree_dp(y);
p=max(p,mx+f[y]+w[i]);
mx=max(mx,f[y]+w[i]);
}
i=s[i][];
}
p=max(mx,p);f[x]=mx;
} int main()
{
int x,y;
cin>>n>>k;
for (int i=;i<n;i++) {
scanf("%d%d",&x,&y);
add(x,y);add(y,x);
}
int ss,t;
ss=bfs();t=bfs(ss);
bfs();
memset(v,,sizeof(v));
if (d[ss]<d[t]) swap(ss,t);
v[ss]=v[t]=;
while (d[ss]>d[t]) {
ss=fa[ss];v[ss]=;++cnt;
}
while (ss!=t) {
ss=fa[ss];t=fa[t];v[ss]=v[t]=;cnt+=;
}
if (k==) {cout<<(n-)*+-cnt<<endl;return ;}
if (cnt==n-) {cout<<n+<<endl;return ;}
mark();tree_dp();
cout<<(n-)*+-cnt-p<<endl;
return ;
}
洛谷 P3629 [APIO2010]巡逻的更多相关文章
- 洛谷 P3629 [APIO2010]巡逻 解题报告
P3629 [APIO2010]巡逻 题目描述 在一个地区中有 n 个村庄,编号为 1, 2, ..., n.有 n – 1 条道路连接着这些村 庄,每条道路刚好连接两个村庄,从任何一个村庄,都可以通 ...
- [洛谷P3629] [APIO2010]巡逻
洛谷题目链接:[APIO2010]巡逻 题目描述 在一个地区中有 n 个村庄,编号为 1, 2, ..., n.有 n – 1 条道路连接着这些村 庄,每条道路刚好连接两个村庄,从任何一个村庄,都可以 ...
- 洛谷P3629 [APIO2010]巡逻(树的直径)
如果考虑不算上新修的道路,那么答案显然为\(2*(n-1)\). 考虑\(k=1\)的情况,会发现如果我们新修建一个道路,那么就会有一段路程少走一遍.这时选择连接树的直径的两个端点显然是最优的. 难就 ...
- BZOJ1912或洛谷3629 [APIO2010]巡逻
一道树的直径 BZOJ原题链接 洛谷原题链接 显然在原图上路线的总长为\(2(n-1)\). 添加第一条边时,显然会形成一个环,而这条环上的所有边全部只需要走一遍.所以为了使添加的边的贡献最大化,我们 ...
- [洛谷P3628] [APIO2010]特别行动队
洛谷题目链接:[APIO2010]特别行动队 题目描述 你有一支由 n 名预备役士兵组成的部队,士兵从 1 到 \(n\) 编号,要将他们拆分 成若干特别行动队调入战场.出于默契的考虑,同一支特别行动 ...
- 洛谷P3628 [APIO2010]特别行动队(动态规划,斜率优化,单调队列)
洛谷题目传送门 安利蒟蒻斜率优化总结 由于人是每次都是连续一段一段地选,所以考虑直接对\(x\)记前缀和,设现在的\(x_i=\)原来的\(\sum\limits_{j=1}^ix_i\). 设\(f ...
- BZOJ1912 APIO2010 洛谷P3629 巡逻
Description: 在一个地区中有 n 个村庄,编号为 1, 2, ..., n.有 n – 1 条道路连接着这些村 庄,每条道路刚好连接两个村庄,从任何一个村庄,都可以通过这些道路到达其 他任 ...
- 【洛谷 P3629】 [APIO2010]巡逻 (树的直径)
题目链接 容易发现,当加一条边时,树上会形成一个环,这个环上的每个点都是只要走一次的,也就是说我们的答案减少了这个环上点的个数,要使答案最小,即要使环上的点最多,求出直径\(L\),则答案为\(2(n ...
- 洛谷 [P3629] 巡逻
树的直径 树的直径有两种求法 1.两遍 dfs 法, 便于输出具体方案,但是无法处理负权边 2.DP 法,代码量少,可以处理负权边 #include <iostream> #include ...
随机推荐
- ElasticSearch Java Api-删除索引
删除可以是删除整个索引库,也可以根据文档id删除索引库下的文档,还可以通过query查询条件删除所有符合条件的数据. 一.删除整个索引库 下面的例子会删除indexName索引: DeleteInde ...
- 文本文件打印类库(C#)
我写了一个打印文本文件的类库,功能包含:打印预览.打印.打印时能够选择打印机.能够指定页码范围. 调用方法很easy: TextFilePrinter p = new TextFilePrinter( ...
- javaweb项目自定义错误页面
当我们把一个web项目成功发布出去,但是有些页面还有待完善的时候,会出现404错误页面.这个会给用户很差的体验.如何将这些错误页面修改为自定义的错误页界面,给用户一些友好的提示呢? 首先我们在web. ...
- Oracle基础 PL-SQL编程基础(2) 分支结构
一.分支结构 1.if语句 语法: IF <布尔表达式> THEN PL/SQL和SQL语句 END IF; 示例: DECLARE v_count NUMBER := &n; B ...
- 使用C++11的function/bind组件封装Thread以及回调函数的使用
之前在http://www.cnblogs.com/inevermore/p/4008572.html中采用面向对象的方式,封装了Posix的线程,那里采用的是虚函数+继承的方式,用户通过重写Thre ...
- Linux——Virtualenv和pip小探
转载自:http://mengzhuo.org/blog/virtualenv%E5%92%8Cpip%E5%B0%8F%E6%8E%A2.html 本文献给那些看着参差不齐的中文文档/教程,但还在坚 ...
- passwd(总结)
1.当前用户是root root用户修改密码 ,直接 passwd[不要输入当前用户密码] 如果修改其他用户密码,需要 passwd 用户名 如: passwd sc 短短的密码,如123也能通过,因 ...
- Android--Handler的用法:在子线程中更新界面
本文主要介绍Android的Handler的用法.Handler能够发送Messsage和Runnable对象到与其相关联的线程的消息队列. 每一个Handler对象与创建它的线程相关联.而且每一个H ...
- OpenLayers 3 之 加入地图鼠标右键菜单
加入右键菜单,首先我们要监听鼠标右键点击的操作,我们知道鼠标右键事件名是 contextmenu.当鼠标在 html 元素之上,点击鼠标右键,便会触发 contextmenu 事件,在 context ...
- ckeditor编辑时 回车 生成一个段落p、解决首行缩进问题
使用ckeditor编辑器时,会自己主动加入上的标签,按回车也会自己主动加入上同样的标签 查看ckeditor编辑器源代码时会看到,点击回车显示的<p> </p> static ...