【題解】bzoj1912(同洛谷P3629)[APIO2010]巡邏 樹的直徑
阿新 • • 發佈:2019-01-30
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。
最初線路總長度為2 * (n-1),當K=1時,找到樹的最長鏈,設長度為L1,答案為2 * (n-1)-L1+1;當K=2時,將直徑邊權取反,再次求直徑,設長度為L2,答案為2*n-L1-L2。
#include<cstdio>
const int N=1e5+10;
struct Edge{
int v,w,nx;
}edge[N<<1];
int head[N],tot=1,n,k,pre1[N],pre2[N],len,rt;
inline void addedge(int u,int v)
{
edge[++tot].v=v;
edge[tot].w=1 ;
edge[tot].nx=head[u];
head[u]=tot;
}
int dfs(int u,int fa)
{
int mx1=0,mx2=0;//最長路與次長路
for(int i=head[u];i;i=edge[i].nx)
{
int v=edge[i].v;
if(v!=fa)
{
int w=edge[i].w+dfs(v,u);
if(w>mx1)mx2=mx1,mx1=w,pre2[u]=pre1[u],pre1[u]=i;
else if(w>mx2)mx2=w,pre2[u]=i;
}
}
if(mx1+mx2>len)len=mx1+mx2,rt=u;
return mx1;
}
int main()
{
//freopen("in.txt","r",stdin);
scanf("%d%d",&n,&k);
int u,v,ans=2*(n-1);
for(int i=1;i<n;i++)
{
scanf("%d%d",&u,&v);
addedge(u,v),addedge(v,u);
}
dfs(1,0);ans=ans-len+1;
if(k==2)
{
len=0;
for(int i=pre1[rt];i;i=pre1[edge[i].v])edge[i].w=edge[i^1].w=-1;//成對變換修改正反邊
for(int i=pre2[rt];i;i=pre1[edge[i].v])edge[i].w=edge[i^1].w=-1;
dfs(1,0);
ans=ans-len+1;
}
printf("%d\n",ans);
return 0;
}
總結
通過邊權取反的方法處理兩個環重疊的情況,非常巧妙。