1. 程式人生 > 實用技巧 >BZOJ2870. 最長道路tree 並查集/邊分治

BZOJ2870. 最長道路tree 並查集/邊分治

題意:

戳這裡

分析:

  • 前置芝士:通道 (可能會做通道的人,也用不著看題解了)

我們利用做通道時的結論:

在一顆邊權均為正的樹上,存在兩個點集

對於一個點集 \(s\) 它的直徑兩端是 \(a,b\)

對於另一個點集 \(t\) 它的直徑兩端是 \(c,d\)

那麼分別以 \(s\)\(t\) 點集中的點為直徑的兩端,這條直徑一定是 \((a,c),(a,d),(b,c),(b,d)\), 中的一條

然後我們就可以通過樹剖預處理做到\(O(log)\) 得到兩個連通塊合併後的直徑

然後我們就按照點權從大向小列舉 \(u\)\(u\) 的出邊,然後合併兩個並查集,順便在合併時更新一下 \(ans\)

tip:

列舉 \(u\) 的出邊時要保證 \(v\) 的點權比 \(u\) 大,因為大的點不會對 \(v[u]\) 產生影響,我們更新的時候預設點權是 \(u\) 的連通塊的最小點權,因為 大的點權不會對小的點權產生影響

  • 另解

邊分治

首先多叉轉二叉(三度化),然後在一側列舉一條邊,在另一側中找出權值比它大的最長鏈,更新答案

遞迴下去,複雜度 \(O(n\log )\)

程式碼:

不會邊分治/kk,只有並查集的程式碼

#include<bits/stdc++.h>
#define pii pair<int,int>
#define mk(x,y) make_pair(x,y)
#define lc rt<<1
#define rc rt<<1|1
#define pb push_back
#define fir first
#define sec second

using namespace std;

namespace zzc
{
	inline int read()
	{
		int x=0,f=1;char ch=getchar();
		while (!isdigit(ch)){if (ch=='-') f=-1;ch=getchar();}
		while (isdigit(ch)){x=x*10+ch-48;ch=getchar();}
		return x*f;
	}
	
	const int maxn = 5e4+5;
	int n,cnt;
	int head[maxn],siz[maxn],v[maxn],p[maxn],son[maxn],fa[maxn],top[maxn],dep[maxn];
	long long ans=0;
	vector<int> g[maxn];
	
	struct edge
	{
		int to,nxt;
	}e[maxn<<1];
	
	void add(int u,int v)
	{
		e[++cnt].to=v;
		e[cnt].nxt=head[u];
		head[u]=cnt;
	}
	
	bool cmp(int x,int y)
	{
		return v[x]>v[y];
	}
	
	void dfs1(int u,int ff)
	{
		siz[u]=1;son[u]=0;fa[u]=ff;dep[u]=dep[ff]+1;
		for(int i=head[u];i;i=e[i].nxt)
		{
			int v=e[i].to;
			if(v==ff) continue;
			dfs1(v,u);
			siz[u]+=siz[v];
			if(siz[son[u]]<siz[v]) son[u]=v;
		}
	}
	
	void dfs2(int u,int bel)
	{
		top[u]=bel;
		if(son[u]) dfs2(son[u],bel);
		for(int i=head[u];i;i=e[i].nxt)
		{
			int v=e[i].to;
			if(v==fa[u]||v==son[u]) continue;
			dfs2(v,v);
		}
	}
	
	int lca(int x,int y)
	{
		while(top[x]!=top[y])
		{
			if(dep[top[x]]<dep[top[y]]) swap(x,y);
			x=fa[top[x]];
		}
		return dep[x]<dep[y]?x:y;
	}
	
	int calc(int x,int y)
	{
		return dep[x]+dep[y]-(dep[lca(x,y)]<<1);
	}
	
	struct node
	{
		int x,y,dis;
		node(){x=0,y=0,dis=0;}
		node(const int &_x,const int &_y){x=_x,y=_y,dis=calc(_x,_y);}
		node(const int &_x,const int &_y,const int &_dis){x=_x,y=_y,dis=_dis;}
	}dp[maxn];
	
	bool operator <(node a,node b)
	{
		return a.dis<b.dis;
	}
	
	node operator + (node a,node b)
	{
		if(!a.x) return b;
		if(!b.x) return a;
		node res=max(a,b);
		res=max(res,max(max(max(node(a.x,b.y),node(a.y,b.x)),node(a.x,b.x)),node(a.y,b.y)));
        return res;
	}
	
	struct dsu
	{
		int fa,val,siz;
	}f[maxn];
	
	int find(int x)
	{
		return f[x].fa==x?x:f[x].fa=find(f[x].fa);
	}
	
	void merge(int x,int y)
	{
		int fx=find(x),fy=find(y);
		if(f[fx].siz<f[fy].siz) swap(fx,fy);
		f[fy].fa=fx;
		f[fx].val=min(f[fx].val,f[fy].val);
		dp[fx]=dp[fx]+dp[fy];
		ans=max(ans,1ll*(dp[fx].dis+1)*f[fx].val);
	}
	
	void work()
	{
		int a,b;
		n=read();
		for(int i=1;i<=n;i++) v[i]=read();
		for(int i=1;i<n;i++)
		{
			a=read();b=read();
			if(v[a]>v[b]) swap(a,b);
			g[a].pb(b);
			add(a,b);add(b,a);
		}
		dfs1(1,0);dfs2(1,1);
		for(int i=1;i<=n;i++) p[i]=i,f[i].fa=i,f[i].val=v[i],f[i].siz=1,dp[i]=node(i,i,0);
		sort(p+1,p+n+1,cmp);
		for(int i=1;i<=n;i++) for(auto v:g[p[i]]) merge(p[i],v);
		printf("%lld\n",ans);
	}

}

int main()
{
	zzc::work();
	return 0;
}