1. 程式人生 > >BZOJ2870—最長道路tree

BZOJ2870—最長道路tree

point 樹鏈剖分 merge script 個數 lse esp i++ 順序

最長道路tree

Description

  H城很大,有N個路口(從1到N編號),路口之間有N-1邊,使得任意兩個路口都能互相到達,這些道路的長度我們視作一樣。每個路口都有很多車輛來往,所以每個路口i都有一個擁擠程度v[i],我們認為從路口s走到路口t的痛苦程度為s到t的路徑上擁擠程度的最小值,乘上這條路徑上的路口個數所得的積。現在請你求出痛苦程度最大的一條路徑,你只需輸出這個痛苦程度。

Simple Description

  給定一棵N個點的樹,求樹上一條鏈使得鏈的長度乘鏈上所有點中的最小權值所得的積最大。其中鏈長度定義為鏈上點的個數。

Input

  第一行N   第二行N個數分別表示1~N的點權v[i]   接下來N-1行每行兩個數x、y,表示一條連接x和y的邊

Output

  一個數,表示最大的痛苦程度。

Sample Input

3
5 3 5
1 2
1 3


Sample Output

10

樣例解釋

  選擇從1到3的路徑,痛苦程度為min(5,5)*2=10

HINT

  100%的數據n<=50000

  其中有20%的數據樹退化成一條鏈

  所有數據點權<=65536

  Hint:建議答案使用64位整型

思路

  首先我們看一下數據範圍,想一想點分治似乎就哈哈了,所以我們需要換一個思路。我們考慮一下,知道只有路徑上的最小值才能對答案有貢獻,所以我們可以把點的權值從大到小排序,這樣我們就可以在插點的同時,維護經過當前點的最長路徑,從而更新路徑最大值就可以了。為什麽呢?因為我們是按照權值由大到小的順序進行的建樹,所以每一次統計路徑時,當前點就是最小值。

  下面我們想,用什麽維護路徑長度呢?樹鏈剖分。用什麽維護最長長度呢?樹的直徑加並查集。所以這些放在一起,就是AC。

代碼

#include <stdio.h>
#include <algorithm>
using namespace std;
#define N 50001
int n,idx,cnt;
int head[N];
int to[N<<1];
int nxt[N<<1];
int val[N],son[N];
int fa[N],top[N];
int f[N],need[N];
int level[N],size[N];
long long ans;int dis[N];
int root1[N],root2[N];
bool vis[N];
bool cmp(const int &a,const int &b)
{return val[a]>val[b];}
int find_anc(int x)
{return (f[x]==x)?x:f[x]=find_anc(f[x]);}
void add(int a,int b)
{
	nxt[++idx]=head[a];
	head[a]=idx;
	to[idx]=b;
}
void dfs(int p,int from)
{
	level[p]=level[from]+1;
	size[p]=1,fa[p]=from;
	for(int i=head[p];i;i=nxt[i])
		if(to[i]!=from)
		{
			dis[to[i]]=dis[p]+1;dfs(to[i],p);
			size[p]+=size[to[i]];
			if(size[son[p]]<size[to[i]]) son[p]=to[i];
		}
}
void dfs2(int p,int from)
{
	if(son[p]) dfs2(son[p],from);
	top[p]=from;
	for(int i=head[p];i;i=nxt[i])
		if(to[i]!=fa[p]&&to[i]!=son[p])
			dfs2(to[i],to[i]);
}
int find_lca(int a,int b)
{
	while(top[a]!=top[b])
	{
		if(level[top[a]]>level[top[b]])
			swap(a,b);
		b=fa[top[b]];
	}
	return (level[a]<level[b])?a:b;
}
int find_dis(int a,int b)
{
	int tmp=find_lca(a,b);
	return dis[a]+dis[b]-2*dis[tmp];
}
void merge(int x,int y)
{
	int fx=find_anc(x);
	int fy=find_anc(y);
	if(fx==fy) return;
	f[fy]=fx;int &p1=root1[fx],&p2=root2[fx];
	int r1=root1[fx],r2=root2[fx],r3=root1[fy],r4=root2[fy];
	int l1=find_dis(r1,r2),l2=find_dis(r1,r3),l3=find_dis(r1,r4);
	int l4=find_dis(r2,r3),l5=find_dis(r2,r4),l6=find_dis(r3,r4);
	int mx=max(l1,max(l2,max(l3,max(l4,max(l5,l6)))));
	if(l1==mx) p1=r1,p2=r2;
	else if(l2==mx) p1=r1,p2=r3;
	else if(l3==mx) p1=r1,p2=r4;
	else if(l4==mx) p1=r2,p2=r3;
	else if(l5==mx) p1=r2,p2=r4;
	else if(l6==mx) p1=r3,p2=r4;
	ans=max(ans,1ll*(mx+1)*val[x]);
}
void add_point(int p)
{
	vis[p]=true;
	for(int i=head[p];i;i=nxt[i])
		if(vis[to[i]]) merge(p,to[i]);
}
int main()
{
	//freopen("Choose.in","r",stdin);
	//freopen("Choose.out","w",stdout);
	scanf("%d",&n);
	for(int i=1;i<=n;i++)
	{
		scanf("%d",&val[i]);
		need[i]=f[i]=root1[i]=root2[i]=i;
	}
	sort(need+1,need+n+1,cmp);
	for(int i=1;i<n;i++)
	{
		int a,b;
		scanf("%d%d",&a,&b);
		add(a,b),add(b,a);
	}
	dfs(1,0),dfs2(1,1);
	for(int i=1;i<=n;i++) add_point(need[i]);
	printf("%I64d",ans);
}

BZOJ2870—最長道路tree