BZOJ2870. 最長道路tree 並查集/邊分治
阿新 • • 發佈:2021-01-07
題意:
分析:
- 前置芝士:通道 (可能會做通道的人,也用不著看題解了)
我們利用做通道時的結論:
在一顆邊權均為正的樹上,存在兩個點集
對於一個點集 \(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; }