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