富有思维性的树形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. CF1175E Minimal Segment Cover 题解

    题意:给出\(n\)个形如\([l,r]\)的线段.\(m\)次询问,每次询问区间\([x,y]\),问至少选出几条线段,使得区间\([x,y]\)的任何一个部位都被至少一条线段覆盖. 首先有一个显然 ...

  2. 未知宽高div水平垂直居中的3种方法

    方法一 <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8 ...

  3. HDU-1263(STL+排序)

    水果 Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others) Total Submiss ...

  4. Codeforces 526G Spiders Evil Plan

    由于做的时候看的是中文题面,第一遍写就被卡题意了:还以为每一条都要过x,那么就是一道动态树根选择2y个叶子的奇怪题目 交完0分gg,才发现题目看错了╮(╯▽╰)╭ the node containin ...

  5. 关于Mybatis的一点小记录(parameterType)

    1.Mybatis的parameterType有两个比较常用的,一个是类的对象,还有一个就是Map,然后取值的方法也很简单: 基本数据类型:#{参数} 获取参数中的值 复杂数据类型:#{属性名} ,m ...

  6. H5 之 Page Visibility

    这个是今天刚发现的,想起之前那个在页面用video标签视频播放,别人切换页面后仍在继续播放,体验很不好,用这个API就可以很完美的解决. Page Visibility API 可以让你获取到这种状态 ...

  7. Java微信公众平台开发(七)--多媒体消息回复之图片回复

    之前我们在做消息回复的时候我们对回复的消息简单做了分类,前面也有讲述如何回复[普通消息类型消息],这里将讲述多媒体消息的回复方法,[多媒体消息]包含回复图片消息/回复语音消息/回复视频消息/回复音乐消 ...

  8. Chess 模拟

    链接:http://acm.hdu.edu.cn/showproblem.php?pid=6114 Problem Description 車是中国象棋中的一种棋子,它能攻击同一行或同一列中没有其他棋 ...

  9. 浅谈Hibernate中的三种数据状态

    Hibernate中的三种数据状态:临时.持久.游离 1.临时态(瞬时态) 不存在于session中,也不存在于数据库中的数据,被称为临时态. 数据库中没有数据与之对应,超过作用域会被JVM垃圾回收器 ...

  10. HDU4576 Robot(概率)

    题意 抄袭自https://www.cnblogs.com/Paul-Guderian/p/7624039.html  多组输入n,m,l,r.表示在一个环上有n个格子.接下来输入m个w表示连续的一段 ...