bzoj1912 [Apio2010]patrol 巡逻

http://www.elijahqi.win/2018/03/06/bzoj1912/
题目描述

在一个地区中有 n 个村庄,编号为 1, 2, …, n。有 n – 1 条道路连接着这些村 庄,每条道路刚好连接两个村庄,从任何一个村庄,都可以通过这些道路到达其 他任一个村庄。每条道路的长度均为 1 个单位。 为保证该地区的安全,巡警车每天要到所有的道路上巡逻。警察局设在编号 为 1 的村庄里,每天巡警车总是从警察局出发,最终又回到警察局。 下图表示一个有 8 个村庄的地区,其中村庄用圆表示(其中村庄 1 用黑色的 圆表示),道路是连接这些圆的线段。为了遍历所有的道路,巡警车需要走的距 离为 14 个单位,每条道路都需要经过两次。

为了减少总的巡逻距离,该地区准备在这些村庄之间建立 K 条新的道路, 每条新道路可以连接任意两个村庄。两条新道路可以在同一个村庄会合或结束 (见下面的图例(c))。 一条新道路甚至可以是一个环,即,其两端连接到同一 个村庄。 由于资金有限,K 只能是 1 或 2。同时,为了不浪费资金,每天巡警车必须 经过新建的道路正好一次。 下图给出了一些建立新道路的例子:

在(a)中,新建了一条道路,总的距离是 11。在(b)中,新建了两条道路,总 的巡逻距离是 10。在(c)中,新建了两条道路,但由于巡警车要经过每条新道路 正好一次,总的距离变为了 15。 试编写一个程序,读取村庄间道路的信息和需要新建的道路数,计算出最佳 的新建道路的方案使得总的巡逻距离最小,并输出这个最小的巡逻距离。

输入输出格式

输入格式:

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

输出格式:

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

输入输出样例

输入样例#1: 复制

8 1
1 2
3 1
3 4
5 3
7 5
8 5
5 6
输出样例#1: 复制

11
输入样例#2: 复制

8 2
1 2
3 1
3 4
5 3
7 5
8 5
5 6
输出样例#2: 复制

10
输入样例#3: 复制

5 2
1 2
2 3
3 4
4 5
输出样例#3: 复制

6
说明

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 然后求最长链 为什么因为无可避免的 曾经走一次的道路 现在不得不重新走两次 那么好办 改成树形dp 类似点分过根的写法 再求一下最长链

为什么我的dfs1 mx1,mx2给的0 因为如果是负数不妨给自环

copy icefox 巨佬的题解 希望它不要打我..

显然答案一开始是n-1<<1,每条边都要走两遍。
如果K=1,可以新建一条道路,那么应该把树上最长链的两端连起来,这样就不用走两遍树上最长链了,而是只需要走一遍,(通过新边直接过去,不需要再返回了)。因此答案就是减去树上最长链+1。
如果K=2,还可以再新建一条,那么我们发现如果这次选择的链和上次选择的最长链有交集,则这一部分的边还是要走两遍,因此我们把最长链上的边权均变为-1,再求树上最长链,答案再减去此时树上最长链+1。
因为有负权,所以我们只能树形dp来求。

#include<cstdio>
#include<algorithm>
#define N 110000
#define inf 0x3f3f3f3f
using namespace std;
inline char gc(){
    static char now[1<<16],*S,*T;
    if (T==S){T=(S=now)+fread(now,1,1<<16,stdin);if (T==S) return EOF;}
    return *S++;
}
inline int read(){
    int x=0,f=1;char ch=gc();
    while(ch<'0'||ch>'9') {if (ch=='-') f=-1;ch=gc();}
    while(ch<='9'&&ch>='0') x=x*10+ch-'0',ch=gc();
    return x*f;
}
struct node{
    int y,next,z;
}data[N<<1];
int h[N],num=1,ed,n,k,dis[N],pos,max1,len;
inline void dfs(int x,int fa){
    for (int i=h[x];i;i=data[i].next){
        int y=data[i].y;if (y==fa) continue;
        dis[y]=dis[x]+data[i].z;if (dis[y]>max1) max1=dis[y],pos=y;dfs(y,x);
    }
}
inline bool mark(int x,int fa){
    if (x==ed) return 1;bool flag=0;
    for (int i=h[x];i;i=data[i].next){
        int y=data[i].y;if (y==fa) continue;
        if (mark(y,x)) {flag=1,data[i].z=-1,data[i^1].z=-1;return flag;}
    }return flag;
}int ans;
inline int dfs1(int x,int fa){
    int mx1=0,mx2=0,tmp=0;
    for (int i=h[x];i;i=data[i].next){
        int y=data[i].y,z=data[i].z;if (y==fa) continue;
        tmp=dfs1(y,x);tmp+=z;
        if (tmp>mx1) mx2=mx1,mx1=tmp;else if (tmp>mx2) mx2=tmp;
    }ans=max(ans,mx1+mx2);return mx1;
}
int main(){
    freopen("bzoj1912.in","r",stdin);
    n=read();k=read();
    for (int i=1;i<n;++i){
        int x=read(),y=read();
        data[++num].y=y;data[num].z=1;data[num].next=h[x];h[x]=num;
        data[++num].y=x;data[num].z=1;data[num].next=h[y];h[y]=num;
    }dfs(1,1);int st=pos;max1=-inf;dis[st]=0;dfs(st,st);ed=pos;len=max1;
    if (k==1) {printf("%d\n",(n-1)*2+1-len);return 0;}mark(st,st);
    dfs1(1,1);printf("%d\n",(n-1)*2+2-len-ans);
    return 0;
}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值