1. 程式人生 > >【BZOJ2870】最長道路tree 點分治+樹狀數組

【BZOJ2870】最長道路tree 點分治+樹狀數組

soft amp 64位 路徑 最小值 tree names out turn

【BZOJ2870】最長道路tree

Description

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

題解:我們采用樹形DP版本的點分治。對於當前的分治中心x,我們依次遍歷它的每個兒子的子樹,每訪問到一個點y,我們記錄y到x路徑上的權值最小值min和長度len,然後在樹狀數組中找到:在以前的子樹中,min>=當前min的len的最大值,然後用min*(len+當前len)更新答案。

這樣正著反著做兩遍即可。

#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
const int maxn=50010;
typedef long long ll;
int n,m,cnt,now,tot,mn,rt;
int to[maxn<<1],next[maxn<<1],head[maxn],siz[maxn],v[maxn],s[70000],vis[maxn],tim[70000],p[maxn];
ll ans;
inline int rd()
{
	int ret=0,f=1;	char gc=getchar();
	while(gc<‘0‘||gc>‘9‘)	{if(gc==‘-‘)f=-f;	gc=getchar();}
	while(gc>=‘0‘&&gc<=‘9‘)	ret=ret*10+gc-‘0‘,gc=getchar();
	return ret*f;
}
void getrt(int x,int fa)
{
	siz[x]=1;
	int i,tmp=0;
	for(i=head[x];i!=-1;i=next[i])	if(!vis[to[i]]&&to[i]!=fa)	getrt(to[i],x),siz[x]+=siz[to[i]],tmp=max(tmp,siz[to[i]]);
	tmp=max(tmp,tot-siz[x]);
	if(tmp<mn)	rt=x,mn=tmp;
}
inline int query(int x)
{
	x++;
	int i,ret=0;
	for(i=x;i<=m;i+=i&-i)
	{
		if(tim[i]<now)	tim[i]=now,s[i]=0;
		ret=max(ret,s[i]);
	}
	return ret;
}
inline void updata(int x,int val)
{
	x++;
	for(int i=x;i;i-=i&-i)
	{
		if(tim[i]<now)	tim[i]=now,s[i]=0;
		s[i]=max(s[i],val);
	}
}
void ask(int x,int fa,int dep,int sn)
{
	sn=min(sn,v[x]),ans=max(ans,(ll)(dep+query(sn))*sn);
	for(int i=head[x];i!=-1;i=next[i])	if(!vis[to[i]]&&to[i]!=fa)	ask(to[i],x,dep+1,sn);
}
void change(int x,int fa,int dep,int sn)
{
	sn=min(sn,v[x]),updata(sn,dep);
	for(int i=head[x];i!=-1;i=next[i])	if(!vis[to[i]]&&to[i]!=fa)	change(to[i],x,dep+1,sn);
}
void solve(int x)
{
	vis[x]=1;
	int i;
	now++,p[0]=0;
	for(i=head[x];i!=-1;i=next[i])	if(!vis[to[i]])	p[++p[0]]=to[i];
	for(i=1;i<=p[0];i++)	ask(p[i],x,2,v[x]),change(p[i],x,1,v[x]);
	now++;
	for(i=p[0];i>=1;i--)	ask(p[i],x,2,v[x]),change(p[i],x,1,v[x]);
	for(i=head[x];i!=-1;i=next[i])	if(!vis[to[i]])	tot=siz[to[i]],mn=1<<30,getrt(to[i],x),solve(rt);
}
inline void add(int a,int b)
{
	to[cnt]=b,next[cnt]=head[a],head[a]=cnt++;
}
int main()
{
	n=rd();
	int i,a,b;
	for(i=1;i<=n;i++)	v[i]=rd(),m=max(m,v[i]+1);
	memset(head,-1,sizeof(head));
	for(i=1;i<n;i++)	a=rd(),b=rd(),add(a,b),add(b,a);
	tot=n,mn=1<<30,ans=m-1,getrt(1,0),solve(rt);
	printf("%lld",ans);
	return 0;
}

【BZOJ2870】最長道路tree 點分治+樹狀數組