1. 程式人生 > >APIO2010巡邏(樹上帶權直徑)

APIO2010巡邏(樹上帶權直徑)

math pac color 巡邏 div pan algo %d algorithm

題目鏈接:https://www.luogu.org/problem/show?pid=3629

題解:

看到這題題解一片空白,身為蒟蒻的我也想為社會做點貢獻……

首先要知道:

1.假如不加邊,每條邊都要走兩次。

2.假如加了一條邊,那麽會形成一個環,而且環上的邊只需要走一次,其余的邊要走兩次。

(自己yy以下就可以知道了)

對於k=1的話,我們就要使環上的邊盡量多,也就是說我們要找樹的直徑,使得樹的直徑在環內。

而對於k=2的話,再加一條邊的時候,會再多一個環。

這時我們要知道:

1.如果一條邊僅在第二個環出現過,只用走一次。

2.如果一條邊在兩個環都出現過,要走兩次。

3.如果一條邊在兩個環都沒出現過,要走兩次。

(自己yy以下就可以知道了)

所以我們給第一個環上的邊-1的權值,其余邊給1的權值,求出樹上權值最大的一條路徑(假直徑O(∩_∩)O)

然後問題就迎刃而解了。

附上蒟蒻的代碼,我的100來行,同一機房神犇的代碼60+行

#include<iostream>
#include<fstream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<cstdlib>
#include<cstring>
using namespace std;
int
n,k,root1,root2,cir,cir2; int cur,head[100010],d[100010],heavy[100010],w[100010]; struct tedge { int to,nex; }e[200010]; struct data { int e,v; }son[100010]; void Add(int u,int v) { cur++; e[cur].to = v; e[cur].nex = head[u]; head[u] = cur; } bool cmp(data a,data b) { return a.e>b.e; } void dfs(int
u,int f) { int cnts=0; for (int i=head[u]; i!=-1; i=e[i].nex) { int v=e[i].to; if (v==f) continue; dfs(v,u); if (d[u]<d[v]+w[v]) { d[u] = d[v]+w[v]; heavy[u] = v; } } for (int i=head[u]; i!=-1; i=e[i].nex) { int v=e[i].to; if (v==f) continue; cnts++; son[cnts].e = d[v]+w[v]; son[cnts].v = v; } sort(son+1,son+1+cnts,cmp); if (cnts==0) d[u] = 0; if (cnts>=1&&cir<son[1].e) { cir = son[1].e; root1 = son[1].v; } if (cnts>=2&&cir<son[1].e+son[2].e) { cir = son[1].e+son[2].e; root1 = son[1].v; root2 = son[2].v; } } void Change() { int u = root1; while (u!=0) { w[u] = -1; u = heavy[u]; } u = root2; while (u!=0) { w[u] = -1; u = heavy[u]; } } int main() { scanf("%d%d",&n,&k); for (int i=0; i<=n; i++) head[i] = -1; for (int i=2; i<=n; i++) w[i] = 1; for (int i=1; i<n; i++) { int x,y; scanf("%d%d",&x,&y); Add(x,y); Add(y,x); } cir = 0; dfs(1,0); if (k==1) { printf("%d\n",2*(n-1)-cir+1); return 0; } Change(); cir2 = cir; cir = -1e9; for (int i=1; i<=n; i++) d[i] = -1e9; dfs(1,0); printf("%d\n",2*(n-1)-cir-cir2+2); return 0; }

APIO2010巡邏(樹上帶權直徑)