富有思维性的树形dp

Description

Input

第一行包含两个整数 n, K(1 ≤ K ≤ 2)。接下来 n – 1行,每行两个整数 a, b, 表示村庄a与b之间有一条道路(1 ≤ a, b ≤ n)。

Output

输出一个整数,表示新建了K 条道路后能达到的最小巡逻距离。

Sample Input

8 1
1 2
3 1
3 4
5 3
7 5
8 5
5 6

Sample Output

11

HINT

10%的数据中,n ≤ 1000, K = 1; 
30%的数据中,K = 1; 
80%的数据中,每个村庄相邻的村庄数不超过 25; 
90%的数据中,每个村庄相邻的村庄数不超过 150; 
100%的数据中,3 ≤ n ≤ 100,000, 1 ≤ K ≤ 2。


题目分析

初看这题觉得毫无头绪,好像怎么也不能把它和最长链联系在一起。特别是新建的边必须经过一次的限制,让人一脸懵逼。

k=0

不过首先挖掘性质:显然的是,若只是树形图,路径最短为$2n-2$;并且实际上起点任意对于答案来说都是一样的。

k=1

然后我们来想一想$k=1$的情况。比如现在我们有一颗树长成这样:

然后我们现在添加一条边:

可以发现形成的环上,若环长度为$lens$,那么需要经过的路径就从$2*lens$变为了$lens+1$。并且对于其他节点来说,它们的花费是不改变的。

由此自然想到我们将最长链的首尾相连,就可以得到$k=1$时的答案。

k=2

有了k=1,扩展至k=2的思路大致相同。除了最长链形成的环,我们需要在树上另找一条次长链。

这里有一个技巧就是把最长链上的边权全都改为-1.引用CQzhangyu的一段话:

一开始想的是将直径拎出来,然后跑一个非常复杂的树形DP,但是看了题解。。。直接将直径上的所有边权值设为-1,再求一遍直径即可。正确性如何保证?如果这两条路径不相交,显然正确;如果相交,那么相当于将原路径拆成了两条。所以做法还是很巧妙的~

还有Coco_T的另一段话

如果我们什么处理都没有,直接求一个次长链(次短路方法), 
可能会和最长链重合,那么最长链上的一部分就会走两遍 
所以我们在求出最长链之后,把最长链上的边权赋为-1, 
这样再跑一个裸的直径就好了 
(这样就可以保证可以在新求出的直径中尽量少重合原先的直径)

其实感觉能够感性理解,但是好像依旧不甚明白……?

还有要注意的是:

     if (k==){
mx = ;
for (int i=s1[dir]; i!=-; i=s1[edges[i].y])
edges[i].val = edges[i^].val = -;
for (int i=s2[dir]; i!=-1; i=s1[edges[i].y])
edges[i].val = edges[i^].val = -;
dfs(, );
ans = ans-mx+;
}

这里第二部分的作用是,将dir的次长链的边权赋为-1.乍一眼看上去好像应该是for (int i=s2[dir]; i!=-1; i=s1[edges[i].y]),不过实际上次长链除了头上是s2,后面的路径走的都是其最大值

 #include<bits/stdc++.h>
const int maxn = ; struct Edge
{
int y,val;
Edge(int a=, int b=):y(a),val(b) {}
}edges[maxn<<];
int n,k,mx,dir,ans;
int edgeTot,nxt[maxn<<],head[maxn];
int s1[maxn],s2[maxn]; int read()
{
char ch = getchar();
int num = ;
bool fl = ;
for (; !isdigit(ch); ch = getchar())
if (ch=='-') fl = ;
for (; isdigit(ch); ch = getchar())
num = (num<<)+(num<<)+ch-;
if (fl) num = -num;
return num;
}
void addedge(int u, int v)
{
edges[++edgeTot] = Edge(v, ), nxt[edgeTot] = head[u], head[u] = edgeTot;
edges[++edgeTot] = Edge(u, ), nxt[edgeTot] = head[v], head[v] = edgeTot;
}
int dfs(int now, int fa)
{
int mx1 = , mx2 = ;
for (int i=head[now]; i!=-; i=nxt[i])
if (edges[i].y!=fa){
int tt = dfs(edges[i].y, now)+edges[i].val;
if (tt > mx1)
mx2 = mx1, mx1 = tt, s2[now] = s1[now], s1[now] = i;
else if (tt > mx2) mx2 = tt, s2[now] = i;
}
if (mx1+mx2 > mx) mx = mx1+mx2, dir = now;
return mx1;
}
int main()
{
memset(head, -, sizeof head);
memset(s1, -, sizeof s1);
memset(s2, -, sizeof s2);
n = read(), k = read();
for (int i=; i<n; i++)
addedge(read(), read());
dfs(, );
ans = *n-mx-;
if (k==){
mx = ;
for (int i=s1[dir]; i!=-; i=s1[edges[i].y])
edges[i].val = edges[i^].val = -;
for (int i=s2[dir]; i!=-; i=s1[edges[i].y])
edges[i].val = edges[i^].val = -;
dfs(, );
ans = ans-mx+;
}
printf("%d\n",ans);
return ;
}

END

【树形dp 最长链】bzoj1912: [Apio2010]patrol 巡逻的更多相关文章

  1. P3565 由简单的树形dp 引入 长链刨分

    这道题感觉不太行 因为自己没想出来. 先说一下暴力吧,取三个点 让两两之间的距离相等怎么做呢,看起来是很复杂的样子的,但是仔细观察发现 答案出自一个点的儿子之间 或者儿子和父亲之间. 暴力枚举三个点然 ...

  2. LOJ3053 十二省联考2019 希望 容斥、树形DP、长链剖分

    传送门 官方题解其实讲的挺清楚了,就是锅有点多-- 一些有启发性的部分分 L=N 一个经典(反正我是不会)的容斥:最后的答案=对于每个点能够以它作为集合点的方案数-对于每条边能够以其两个端点作为集合点 ...

  3. BZOJ1912 [Apio2010]patrol 巡逻

    本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000作者博客:http://www.cnblogs.com/ljh2000-jump/转 ...

  4. 2018.11.06 bzoj1912: [Apio2010]patrol 巡逻(树形dp)

    传送门 一道挺妙的题啊. 对于K==1K==1K==1的直接求树的直径. 对于K==2K==2K==2的先求一次直径,然后考虑到如果两条边加进去形成的两个环重叠就会有负的贡献. 因此把之前那条直径上的 ...

  5. BZOJ1912:[APIO2010]patrol巡逻

    Description Input 第一行包含两个整数 n, K(1 ≤ K ≤ 2).接下来 n – 1行,每行两个整数 a, b, 表示村庄a与b之间有一条道路(1 ≤ a, b ≤ n). Ou ...

  6. 【BZOJ1912】[Apio2010]patrol 巡逻 树形DP

    [BZOJ1912][Apio2010]patrol 巡逻 Description Input 第一行包含两个整数 n, K(1 ≤ K ≤ 2).接下来 n – 1行,每行两个整数 a, b, 表示 ...

  7. 【BZOJ-1912】patrol巡逻 树的直径 + DFS(树形DP)

    1912: [Apio2010]patrol 巡逻 Time Limit: 4 Sec  Memory Limit: 64 MBSubmit: 1034  Solved: 562[Submit][St ...

  8. [Apio2010]patrol 巡逻

    1912: [Apio2010]patrol 巡逻 Time Limit: 4 Sec  Memory Limit: 64 MBSubmit: 2541  Solved: 1288[Submit][S ...

  9. BZOJ 1912:[Apio2010]patrol 巡逻(树直径)

    1912: [Apio2010]patrol 巡逻 Input 第一行包含两个整数 n, K(1 ≤ K ≤ 2).接下来 n – 1行,每行两个整数 a, b, 表示村庄a与b之间有一条道路(1 ≤ ...

随机推荐

  1. IT兄弟连 JavaWeb教程 Servlet会话跟踪 经典面试题

    1.描述Cookie的作用 Cookie是网站保存在浏览器客户端的信息,也就是说保存在访客的机器里的变量,一般随着HTTP头发送到客户端.在Cookie生效之后及失效之前,客户每次发出页面请求的时候, ...

  2. Oracle Java SE 组件概念图

    JDK1.8 组件概念图

  3. 网络装机pxe服务器的配置过程

    网络装机pxe服务器的配置过程 背景: 针对于Linux运维工作中遇到的需要大批量安装Linux系统的情况,通过网络装机的方式实现无人值守安装Linux操作系统,现需要配置一台pxe服务器用于pxe批 ...

  4. Execution failed for task ':app:lintVitalRelease'.

    解决方法:在build.gradle文件的android部分添加如下代码: lintOptions { checkReleaseBuilds false abortOnError false} 最后成 ...

  5. ssrs 小技巧

    1. 使用 RowNumber() 增加行号 =RowNumber("DataSet1") 2. 单双行 变色 =iif(RowNumber(Nothing) Mod 2, &qu ...

  6. JAVA Debug调试技术

    System.out.println(e),这个方法打印出异常,并且输出在哪里出现的异常,不过它和另外一个e.printStackTrace()方法不同.后者也是打印出异常,但是它还将显示出更深的调用 ...

  7. python+selenium 页面中存在选项卡时,获取页面内容的小技巧

    最近用selenium读取页面内容时,遇到包含选项卡的页面,由于选项卡多由js加载其中的内容,所以在网址打开时只能获取到默认显示的选项卡中的内容,而tab2.tab3等等都需要傻傻的点击一下才会获取到 ...

  8. 090 Subsets II 子集 II

    给定一个可能包含重复整数的列表,返回所有可能的子集(幂集).注意事项:解决方案集不能包含重复的子集.例如,如果 nums = [1,2,2],答案为:[  [2],  [1],  [1,2,2],  ...

  9. Unity加载AssetBundle的方法

    using System.Collections; using System.Collections.Generic; using UnityEngine; using System.IO; usin ...

  10. 绘制复杂的原理图元件和pcb封装库用于cadence(一)

    绘制TI公司的TPS53319电源芯片封装 由于产品设计需要大电流电源供电,选用TI公司TPS53319电源芯片通过cadence软件进行电路设计,但是TI公司所提供的封装格式为CAD File(.b ...