题目在这里

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

我们往简单的想,不建立新的道路时,从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. 自定义ViewPager的兼容性问题及解决办法

    通过它我们可以给图片增加组合动画效果,也可以写成一个图片查看器. 比如我们首次安装应用的时候,很多就会用到ViewPager给我们做一个应用简介.今天要写的也是这个--怎么用ViewPager实现动画 ...

  2. 从程序员到asp.net架构师转变[转]

    微软的DotNet开发绝对是属于那种入门容易提高难的技术.而要能够成为DotNet架构师没有三年或更长时间的编码积累基本上是不可能的.特别是在大型软件项目中,架构师是项目核心成员,承上启下,因此 RU ...

  3. EFFECTIVE JAVA 第一天 静态工厂方法

    静态工厂方法:(这里指的是就是普通static方法),类可以通过静态工厂方法提供给它的客户端,而不是通过构造器.提供静态工厂方法而不是公有构造器,这样做有几大优势. 在类的实现中使用了API的类被称为 ...

  4. PATHINFO模式是thinkphp特有的吗?

    pathinfo当然不是某个框架特有的,pathinfo严格上讲是HTTP服务器提供的一个预定义变量,在许多的框架中有一个重要的组件叫做路由器,这个组件可以通过使用pathinfo来实现. 考虑以下代 ...

  5. [LeetCode]Palindrome Number 推断二进制和十进制是否为回文

    class Solution { public: bool isPalindrome2(int x) {//二进制 int num=1,len=1,t=x>>1; while(t){ nu ...

  6. jquery的push()

    JavaScript push() 方法 JavaScript Array 对象 定义和用法 push() 方法可向数组的末尾添加一个或多个元素,并返回新的长度. 语法 arrayObject.pus ...

  7. SpringBoot项目的云服务器部署

    1.场景还原 springboot配置相当简单,人人皆知.怎么把springboot工程部署到云服务器上呢?可能有人会说,博主你前篇不是讲了java工程的云部署把:但是我想澄清一点的是,我前篇的工程都 ...

  8. php正則表達式中的非贪婪模式匹配的使用

    php正則表達式中的非贪婪模式匹配的使用 通常我们会这么写: $str = "http://www.baidu/.com? url=www.sina.com/"; preg_mat ...

  9. Frogger - poj 2253 (Dijkstra)

      Time Limit: 1000MS   Memory Limit: 65536K Total Submissions: 28802   Accepted: 9353 Description Fr ...

  10. .net 字符串驻留

    .net中的string表达的是常量字符串. JIT编译器编译时判断遇到的常量字符串是否在内部散列表中,如果不在,添加进去.当第一次执行到含字符串的方法时,CLR会检查该字符串是否在内部的一个散列表中 ...