P2387 [NOI2014]魔法森林 LCT维护边权

题意:

戳这里

分析:

首先我们发现需要按 (a,b) 的大小动态加减边,直到 1 和 (n) 联通,所以上 LCT

具体做法就是,把边按 (a) 升序排好后,从小到大加边,每次新加入一条边 (u -> v) 时,分两种情况:

  1. 在同一个连通块内

此时删掉连通块里 b 最大的那条边,然后加入这条边

  1. 不在一个连通块内部

直接 (link) 就好了

然鹅,我们发现一个大问题,LCT 是用来维护点权的,而我们需要对边权进行查询,怎么办呢?很简单,就是新建一个节点表示边

对于 (u->v) 等价于 (link(i,u),link(i,v)) 把边权转化为 (i) 的点权

代码:

#include<bits/stdc++.h>
#define pii pair<int,int>
#define mk(x,y) make_pair(x,y)
#define fir first
#define sec second
using namespace std;

namespace zzc
{
	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 = 2e5+5;
	int n,m,cnt,ans=0x3f3f3f;
	pii tmp[maxn];
	struct edge
	{
		int frm,to,va,vb;
	}e[maxn];
	
	bool cmp(edge a,edge b)
	{
		return a.va==b.va?a.vb<b.vb:a.va<b.va;
	}
	
	namespace lct
	{
		int top,q[maxn],fa[maxn],c[maxn][2],rev[maxn];
		pii val[maxn];
		void pushup(int x){val[x]=max(tmp[x],max(val[c[x][0]],val[c[x][1]]));}
		void pushdown(int x)
		{
			int l=c[x][0],r=c[x][1];
			if(rev[x])
			{
				rev[l]^=1;rev[r]^=1;rev[x]^=1;
				swap(c[x][0],c[x][1]);
			}
		}
		bool isroot(int x){return c[fa[x]][0]!=x&&c[fa[x]][1]!=x;}
		void rotate(int x)
		{
			int y=fa[x],z=fa[y],l,r;
			if(c[y][0]==x)l=0;else l=1;r=l^1;
			if(!isroot(y)){if(c[z][0]==y)c[z][0]=x;else c[z][1]=x;}
			fa[y]=x;fa[x]=z;fa[c[x][r]]=y;
			c[y][l]=c[x][r];c[x][r]=y;
			pushup(y);pushup(x);
		}
		void splay(int x)
		{
			top=1;q[top]=x;
			for(int i=x;!isroot(i);i=fa[i]) q[++top]=fa[i];
			for(int i=top;i;i--) pushdown(q[i]);
			while(!isroot(x))
			{
				int y=fa[x],z=fa[y];
				if(!isroot(y))
				{
					if((c[z][0]==y)^(c[y][0]==x)) rotate(x);
					else rotate(y);
				}
				rotate(x);
			}
		}
		void access(int x){for(int t=0;x;t=x,x=fa[x])splay(x),c[x][1]=t,pushup(x);}
		void makeroot(int x){access(x);splay(x);rev[x]^=1;}
		int find(int x){access(x);splay(x);while(c[x][0])x=c[x][0];splay(x);return x;}
		void split(int x,int y){makeroot(x);access(y);splay(y);}
		void cut(int x,int y){split(x,y);if(c[y][0]==x&&c[x][1]==0)c[y][0]=0,fa[x]=0,pushup(y);}
		void link(int x,int y){makeroot(x);fa[x]=y;}
	}
	using namespace lct;
	
	void work()
	{
		n=read();m=read();
		for(int i=1;i<=m;i++)e[i].frm=read(),e[i].to=read(),e[i].va=read(),e[i].vb=read();
		sort(e+1,e+m+1,cmp);
		for(int i=1;i<=m;i++)
		{
			tmp[i+n].fir=e[i].vb;tmp[i+n].sec=i+n;
			int u=e[i].frm,v=e[i].to;
			bool flag=true;
			makeroot(u);
			if(find(u)==find(v))
			{
				split(u,v);
				int id=val[v].sec;
				if(tmp[id].fir>e[i].vb)
				{
					cut(id,e[id-n].frm);
					cut(id,e[id-n].to);
				}
				else flag=false;
			}
			if(flag) link(i+n,u),link(i+n,v);
			makeroot(1);
			if(find(n)==1) 
			{
				split(1,n),ans=min(ans,e[i].va+val[n].fir);
			}
		}
		if(ans==0x3f3f3f) printf("-1
");
		else printf("%d
",ans);
	}
	
}

int main()
{
	zzc::work();
	return 0;
}
原文地址:https://www.cnblogs.com/youth518/p/14111469.html