P2387 [NOI2014]魔法森林

LCT边权维护经典题

咋维护呢?边化为点,边权变点权。

本题中我们把边对关键字A进行排序,动态维护关键字B的最小生成树

加边后出现环咋办?

splay维护最大边的编号,找到最大边删除再加新边就ok辣

#include<cstdio>
#include<algorithm>
using namespace std;
inline void Swap(int &a,int &b){a^=b^=a^=b;}
inline int Min(int a,int b){return a<b?a:b;}
void read(int &x){
static char c=getchar();x=;
while(c<''||c>'') c=getchar();
while(''<=c&&c<='') x=x*+(c^),c=getchar();
}
#define N 200005
struct edge{int f,t,A,B;}a[N];
int n,m,ans=2e9,ch[N][],fa[N],val[N],s[N],rev[N],dad[N];
#define lc ch[x][0]
#define rc ch[x][1]
inline bool cmp(edge P,edge Q){return P.A<Q.A;}
int find(int x){return dad[x]==x?x:dad[x]=find(dad[x]);}
inline bool nrt(int x){return ch[fa[x]][]==x||ch[fa[x]][]==x;}
void up(int x){//维护平衡树上最大点权的点的编号(就是边化为的点)
s[x]=x;
if(val[s[lc]]>val[s[x]]) s[x]=s[lc];
if(val[s[rc]]>val[s[x]]) s[x]=s[rc];
}
void Rev(int x){Swap(lc,rc); rev[x]^=;}
void down(int x){if(rev[x]) Rev(lc),Rev(rc),rev[x]=;}
void Pre(int x){if(nrt(x))Pre(fa[x]); down(x);}
void turn(int x){
int y=fa[x],z=fa[y],l=(ch[y][]==x),r=l^;
if(nrt(y)) ch[z][ch[z][]==y]=x;
fa[ch[x][r]]=y; fa[y]=x; fa[x]=z;
ch[y][l]=ch[x][r]; ch[x][r]=y;
up(y); up(x);
}
void splay(int x){
Pre(x);
for(;nrt(x);turn(x)){
int y=fa[x],z=fa[y];
if(nrt(y)) turn(((ch[z][]==y)^(ch[y][]==x))?x:y);
}
}
void access(int x){for(int y=;x;y=x,x=fa[x]) splay(x),rc=y,up(x);}
void makert(int x){access(x);splay(x);Rev(x);}
int findrt(int x){
access(x);splay(x);down(x);
while(lc) x=lc,down(x);
splay(x); return x;
}
void link(int x,int y){makert(x); if(findrt(y)!=x)fa[x]=y;}
void cut(int x,int y){
makert(x);
if(findrt(y)==x&&fa[y]==x&&!ch[y][]) fa[y]=rc=,up(x);
}
void split(int x,int y){makert(x);access(y);splay(y);}
int main(){
read(n);read(m); register int i;
for(i=;i<=n+m;++i) dad[i]=i;
for(i=;i<=m;++i)
read(a[i].f),read(a[i].t),read(a[i].A),read(a[i].B);
sort(a+,a+m+,cmp);
for(i=;i<=m;++i) val[n+i]=a[i].B;
for(i=;i<=m;++i){
int r1=find(a[i].f),r2=find(a[i].t),e;//并查集维护是否连通
if(r1==r2){
split(a[i].f,a[i].t); e=s[a[i].t];
if(val[e]<=a[i].B) continue;
cut(a[e-n].f,e); cut(e,a[e-n].t);//删掉最大边
}else dad[r1]=r2;
link(a[i].f,i+n); link(i+n,a[i].t);
if(find()==find(n)) split(n,),ans=Min(ans,a[i].A+val[s[]]);
}
if(ans>=2e9) puts("-1");
else printf("%d",ans);
return ;
}
05-08 15:38