【BZOJ 1912】 [Apio2010]patrol 巡逻

1912: [Apio2010]patrol 巡逻

Time Limit: 4 Sec Memory Limit: 64 MB

Submit: 669 Solved: 380

[Submit][Status][Discuss]

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=1,显然是要求树的直径(因为在环上的边只需要走一次)。

②如果k=2,我们需要求加入两条边组成的环最大。

换个角度来看,相当于第一次找到直径之后进行第二次寻找,如果第二次找直径包含第一次找到的边,那么这条边对总答案的贡献为?1。

所以只要在第一次直径之后把直径上的边权置为?1,然后再找一次直径即可。

#include <iostream>
#include <algorithm>
#include <cstring>
#include <cmath>
#include <cstdlib>
#include <cstdio>
#define M 200005
#define inf 0x3f3f3f3f
using namespace std;
int from,to,h[M],d[M],ans2,p[M],f[M],k,n,tot;
struct edge
{
    int y,ne,v;
}e[M];
void Addedge(int x,int y)
{
    e[++tot].y=y;
    e[tot].ne=h[x];
    e[tot].v=1;
    h[x]=tot;
}
void dfs(int x,int fa)
{
    for (int i=h[x];i;i=e[i].ne)
    {
        int y=e[i].y;
        if (y==fa) continue;
        d[y]=d[x]+e[i].v;
        p[y]=i;
        f[y]=x;
        dfs(y,x);
    }
}
int Getdiameter()
{
    d[1]=0;
    dfs(1,0);
    from=0,to=0;
    for (int i=1;i<=n;i++)
        if (d[from]<d[i]) from=i;
    d[from]=0;
    p[from]=f[from]=0;
    dfs(from,0);
    for (int i=1;i<=n;i++)
        if (d[to]<d[i]) to=i;
    return d[to]-1;
}
void dfs2(int x,int fa)
{
    int m1=-inf,m2=-inf;
    for (int i=h[x];i;i=e[i].ne)
    {
        int y=e[i].y;
        if (y==fa) continue;
        dfs2(y,x);
        d[y]+=e[i].v;
        d[x]=max(d[x],d[y]);
        if (d[y]>m1) m2=m1,m1=d[y];
        else if (d[y]>m2) m2=d[y];
    }
    if (m1==-inf&&m2==-inf)
        d[x]=0;
    ans2=max(ans2,m1+m2-1);
}
int main()
{
    tot=1;
    scanf("%d%d",&n,&k);
    for (int i=1;i<n;i++)
    {
        int x,y;
        scanf("%d%d",&x,&y);
        Addedge(x,y),Addedge(y,x);
    }
    int ans=2*(n-1)-Getdiameter();
    if (k==2)
    {
        int x=p[to];
        while (x)
        {
            e[x].v=e[x^1].v=-1;
            x=p[f[e[x].y]];
        }
        ans2=0;
        for (int i=1;i<=n;i++)
            d[i]=-inf;
        dfs2(1,0);
        ans-=ans2;
    }
    cout<<ans<<endl;
    return 0;
}

时间: 2024-11-11 17:28:53

【BZOJ 1912】 [Apio2010]patrol 巡逻的相关文章

bzoj 1912 : [Apio2010]patrol 巡逻 树的直径

题目链接 如果k==1, 显然就是直径. k==2的时候, 把直径的边权变为-1, 然后在求一次直径. 变为-1是因为如果在走一次这条边, 答案会增加1. 学到了新的求直径的方法... #include <bits/stdc++.h> using namespace std; #define pb(x) push_back(x) #define ll long long #define mk(x, y) make_pair(x, y) #define lson l, m, rt<<

bzoj 1912: [Apio2010]patrol 巡逻

呵呵呵呵呵呵,自己画图,大概半个小时,觉的连上边会成环(是不是该交仙人掌了??)然后求环不重合部分最大就好了, 结果写了一坨DP,最后写不下去了,再次扒了题解. 发现我真的是个sb. k==1,直接是直径 k==2,搞出直径然后把直径删掉(把权值赋为-1,再找直径)(有点像我一开始想的每次找个最长链去贪心,然而,,总觉得,这种题贪心这么可能对) 1 /*#include <bits/stdc++.h> 2 #define LL long long 3 #define lowbit(x) x&a

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

1912: [Apio2010]patrol 巡逻 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

【bzoj1912】 Apio2010—patrol 巡逻

http://www.lydsy.com/JudgeOnline/problem.php?id=1912 (题目链接) 题意 给出一棵树,要求在树上添加K(1 or 2)条边,添加的边必须经过一次,使得从1号节点到达每个节点最后返回1号节点所经过的路径最短. Solution 如果不添加边,那么答案一定是每条边经过两次. 如果K为1,那么答案就很明显对吧,找到树的直径,链接直径两端点,使得直径上的边只经过一次,答案最优. 那么如果K为2,我们会发现,当两个环有变重叠时,重叠的边同样是要经过2次.

BZOJ1912 [Apio2010]patrol 巡逻

本文版权归ljh2000和博客园共有,欢迎转载,但须保留此声明,并给出原文链接,谢谢合作. 本文作者:ljh2000作者博客:http://www.cnblogs.com/ljh2000-jump/转载请注明出处,侵权必究,保留最终解释权! Description Input 第一行包含两个整数 n, K(1 ≤ K ≤ 2).接下来 n – 1行,每行两个整数 a, b, 表示村庄a与b之间有一条道路(1 ≤ a, b ≤ n). Output 输出一个整数,表示新建了K 条道路后能达到的最小

P1912: [Apio2010]patrol 巡逻

这道题讨论了好久,一直想不明白,如果按传统的随便某一个点出发找最长链,再回头,K=2 的时候赋了-1就没法用这种方法找最长链了,于是乎,更强的找最长链的方法就来了..类似于DP的东西吧.先上代码: 1 const maxn=100002; 2 type 3 node=record 4 f,t,l:longint; 5 end; 6 var n,k,i,j,ans,num,f,t,diameter,s,sum:longint; 7 b:array[0..2*maxn] of node; 8 hea

【BZOJ】【1912】【APIO2010】patrol巡逻

树形DP 说是树形DP,其实就是求树的最长链嘛…… K=1的时候明显是将树的最长链的两端连起来最优. 但是K=2的时候怎么搞? 考虑第一次找完树的最长链以后的影响:第一次找过的边如果第二次再走,对答案的贡献会变成-1,因为两次都选这一段的话,反而会使得这一段不得不走两次(如果只被选一次的话就可以只走一次),所以就将第一次找出的树的最长链上的边权值都改为-1.这个操作可以用链表实现(类比一下最小费用最大流的spfa实现!) 题解:http://blog.csdn.net/qpswwww/artic

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

1912: [Apio2010]patrol 巡逻 Time Limit: 4 Sec  Memory Limit: 64 MBSubmit: 1034  Solved: 562[Submit][Status][Discuss] Description Input 第一行包含两个整数 n, K(1 ≤ K ≤ 2).接下来 n – 1行,每行两个整数 a, b, 表示村庄a与b之间有一条道路(1 ≤ a, b ≤ n). Output 输出一个整数,表示新建了K 条道路后能达到的最小巡逻距离.

BZOJ 1912 巡逻(树直径)

题目链接:http://61.187.179.132/JudgeOnline/problem.php?id=1912 题意:给出一棵树,边权为1.现在加一条或两条边后,使得从1出发遍历每个点至少一次再回到1的路程最短. 思路:先求一次树的直径Max1.然后将直径的边权改为-1,再求一次直径Max2.答案为ans=(n-1)*2-(Max1-1)-(Max2-1). struct node { int u,v,w,next; }; node edges[N<<1]; int head[N],e;