题目在这里

这是一个紫题,当然很难。

我们往简单的想,不建立新的道路时,从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]巡逻的更多相关文章

  1. 洛谷 P3629 [APIO2010]巡逻 解题报告

    P3629 [APIO2010]巡逻 题目描述 在一个地区中有 n 个村庄,编号为 1, 2, ..., n.有 n – 1 条道路连接着这些村 庄,每条道路刚好连接两个村庄,从任何一个村庄,都可以通 ...

  2. [洛谷P3629] [APIO2010]巡逻

    洛谷题目链接:[APIO2010]巡逻 题目描述 在一个地区中有 n 个村庄,编号为 1, 2, ..., n.有 n – 1 条道路连接着这些村 庄,每条道路刚好连接两个村庄,从任何一个村庄,都可以 ...

  3. 洛谷P3629 [APIO2010]巡逻(树的直径)

    如果考虑不算上新修的道路,那么答案显然为\(2*(n-1)\). 考虑\(k=1\)的情况,会发现如果我们新修建一个道路,那么就会有一段路程少走一遍.这时选择连接树的直径的两个端点显然是最优的. 难就 ...

  4. BZOJ1912或洛谷3629 [APIO2010]巡逻

    一道树的直径 BZOJ原题链接 洛谷原题链接 显然在原图上路线的总长为\(2(n-1)\). 添加第一条边时,显然会形成一个环,而这条环上的所有边全部只需要走一遍.所以为了使添加的边的贡献最大化,我们 ...

  5. [洛谷P3628] [APIO2010]特别行动队

    洛谷题目链接:[APIO2010]特别行动队 题目描述 你有一支由 n 名预备役士兵组成的部队,士兵从 1 到 \(n\) 编号,要将他们拆分 成若干特别行动队调入战场.出于默契的考虑,同一支特别行动 ...

  6. 洛谷P3628 [APIO2010]特别行动队(动态规划,斜率优化,单调队列)

    洛谷题目传送门 安利蒟蒻斜率优化总结 由于人是每次都是连续一段一段地选,所以考虑直接对\(x\)记前缀和,设现在的\(x_i=\)原来的\(\sum\limits_{j=1}^ix_i\). 设\(f ...

  7. BZOJ1912 APIO2010 洛谷P3629 巡逻

    Description: 在一个地区中有 n 个村庄,编号为 1, 2, ..., n.有 n – 1 条道路连接着这些村 庄,每条道路刚好连接两个村庄,从任何一个村庄,都可以通过这些道路到达其 他任 ...

  8. 【洛谷 P3629】 [APIO2010]巡逻 (树的直径)

    题目链接 容易发现,当加一条边时,树上会形成一个环,这个环上的每个点都是只要走一次的,也就是说我们的答案减少了这个环上点的个数,要使答案最小,即要使环上的点最多,求出直径\(L\),则答案为\(2(n ...

  9. 洛谷 [P3629] 巡逻

    树的直径 树的直径有两种求法 1.两遍 dfs 法, 便于输出具体方案,但是无法处理负权边 2.DP 法,代码量少,可以处理负权边 #include <iostream> #include ...

随机推荐

  1. javascript 中event是全局变量

    The only thing I can think of is that event is in fact window.event and it makes itself available wh ...

  2. ionic准备之angular基础——$watch,$apply,$timeout方法(5)

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...

  3. C. Glass Carving (CF Round #296 (Div. 2) STL--set的运用 &amp;&amp; 并查集方法)

    C. Glass Carving time limit per test 2 seconds memory limit per test 256 megabytes input standard in ...

  4. C语言 | 计算器实现(中缀表示法/后缀表示法)

    ———————————————————————————————————————————— 实现原理: 每个操作数都被依次压入栈中,当一个运算符到达时,从栈中弹出相应数目的操作数(对于二元运算符来说是两 ...

  5. JavaScript 转换小技巧

    1.变量转换 看起来很简单,但据我所看到的,使用构造函数,像Array()或者Number()来进行变量转换是常用的做法.始终使用原始数据类型(有时也称为字面量)来转换变量,这种没有任何额外的影响的做 ...

  6. 微信小程序登录JAVA后台

    代码地址如下:http://www.demodashi.com/demo/12736.html 登录流程时序登录流程时序 具体的登录说明查看 小程序官方API 项目的结构图: springboot项目 ...

  7. (三)storm-kafka源代码走读之怎样构建一个KafkaSpout

    上一节介绍了config的相关信息,这一节说下,这些參数各自是什么.在zookeeper中的存放路径是如何的,之前QQ群里有非常多不知道该怎么传入正确的參数来new 一个kafkaSpout,其主要还 ...

  8. BigDecimal舍入模式使用及建议

    1. 八种舍入模式 此节内容参考于 https://my.oschina.net/sunchp/blog/670909. JDK1.5发布的枚举 RoundingMode 对 BigDecimal 的 ...

  9. java代理ip有效检测

    java实现代理ip有效检测,依赖Apache的HttpClient   正式版:       /**        * 批量代理IP有效检测        *        * @param pro ...

  10. android ListView滚动条监听判断滚动到底部还是顶部

    代码: lv.setOnScrollListener(new OnScrollListener() { public void onScrollStateChanged(AbsListView vie ...