洛谷2387 NOI2014魔法森林(LCT维护最小生成树)

本题是运用LCT来维护一个最小生成树。
是一个经典的套路

题目中求的是一个\(max(a_i)+max(b_i)\)尽可能小的路径。

那么这种的一个套路就是,先按照一维来排序,然后用LCT维护另一维

那么这个对于这个题来说,我们考虑,可以先按照a从小到大排序,然后顺次加入每条边,这样每次加入的边一定是有可能会更新到\(ans\)的.

对于一条边\(u->v\),如果\(u\)\(v\)不在一个联通块里面的话,那么就直接连上这个边,然后尝试更新答案

如果在同一个联通块里面呢,我们就判断\(u\)\(v\)的路径上的\(b\)值的最大值,如果小于当前的边的\(b\),那么这条边就有可能会更新答案,所以就把原来的边删掉,然后\(link\)当前边。
不过一个需要注意的地方就是
每次不管是加入或者不加入,都需要对\(ans\)进行更新(不需要担心答案的覆盖,因为不优的答案永远是会被优的答案提前更新到一次的)

同时维护边的时候,我是选择了\(map\)

上代码

// luogu-judger-enable-o2
#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<vector>
#include<queue>
#include<cmath>
#include<map>
#include<set>

using namespace std;

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<<1)+(x<<3)+ch-'0';ch=getchar();}
  return x*f;
}

const int maxn = 4e5+1e2;

struct Node{
	int x,y,a,b;
};

Node a[maxn];

int ch[maxn][3];
int fa[maxn],rev[maxn];
int mx[maxn],mxpos[maxn];
int val[maxn];
int n,m;

int son(int x)
{
	if (ch[fa[x]][0]==x) return 0;
	else return 1;
}

bool notroot(int x)
{
	return ch[fa[x]][0]==x || ch[fa[x]][1]==x;
}

void update(int x)
{
	mx[x]=val[x];
	mxpos[x]=x;
	if (ch[x][0])
	{
		if (mx[ch[x][0]]>mx[x])
		{
			mx[x]=mx[ch[x][0]];
			mxpos[x]=mxpos[ch[x][0]];
		}
	}
	if (ch[x][1])
	{
		if (mx[ch[x][1]]>mx[x])
		{
			mx[x]=mx[ch[x][1]];
			mxpos[x]=mxpos[ch[x][1]];
		}
	}
}

void reverse(int x)
{
	swap(ch[x][0],ch[x][1]);
	rev[x]^=1;
}

void pushdown(int x)
{
	if (rev[x])
	{
		if (ch[x][1]) reverse(ch[x][1]);
		if (ch[x][0]) reverse(ch[x][0]);
		rev[x]=0;
	}
}

void rotate(int x)
{
	int y=fa[x],z=fa[y];
	int b=son(x),c=son(y);
	if (notroot(y)) ch[z][c]=x;
	fa[x]=z;
	ch[y][b]=ch[x][!b];
	fa[ch[x][!b]]=y;
	ch[x][!b]=y;
	fa[y]=x;
	update(y);
    update(x);
}

int st[maxn];

void splay(int x)
{
  int y=x,cnt=0;
  st[++cnt]=y;
  while (notroot(y)) y=fa[y],st[++cnt]=y;
  while (cnt) pushdown(st[cnt--]);
  while (notroot(x))
  {
  	int y=fa[x],z=fa[y];
  	int b=son(x),c=son(y);
  	if (notroot(y))
  	{
  		if (b==c) rotate(y);
  		else rotate(x);
	}
	rotate(x);
  }
  update(x);
}

void access(int x)
{
	for (int y=0;x;y=x,x=fa[x])
	{
		splay(x);
		ch[x][1]=y;
		update(x);
	}
	//for(expose();pfa;splay()) pfa->expose(),pfa->set_ch(1,this),pfa=0;
	//expose(x);while(splice(x));return 0;
}

void makeroot(int x)
{
	access(x);
	splay(x);
	reverse(x);
}

int findroot(int x)
{
	access(x);
	splay(x);
	while (ch[x][0])
	{
		pushdown(x);
		x=ch[x][0];
	}
	return x;
}

void split(int x,int y)
{
	makeroot(x);
	access(y);
	splay(y);
}

void link(int x,int y)
{
	makeroot(x);
	if (findroot(y)!=x) fa[x]=y;
}

void cut(int x,int y)
{
	split(x,y);
	if (ch[x][0] || ch[x][1] || fa[x]!=y || ch[y][son(x)^1]) return;
	fa[x]=ch[y][0]=0;
}

int ans=1e9;

bool cmp(Node a,Node b)
{
	return a.a<b.a;
}

int main()
{
   n=read(),m=read();
   for (int i=1;i<=m;i++)
   {
   	  a[i].x=read(),a[i].y=read();
   	  a[i].a=read(),a[i].b=read();
   }
   sort(a+1,a+1+m,cmp);
   for (int i=1;i<=m;i++)
   {
   	   val[i+n]=a[i].b;
   	   if (findroot(a[i].x)==findroot(a[i].y))
   	   {
   	   	  split(a[i].x,a[i].y);
   	   	  int now = mxpos[a[i].y];
   	   	  if (mx[a[i].y]<a[i].b) continue;
   	   	  now-=n;
   	   	  cut(a[now].x,now+n);
   	   	  cut(a[now].y,now+n);
   	   	  //cout<<a[now].x<<" "<<a[now].y<<endl;
   	   	  link(a[i].x,i+n);
   	   	  link(a[i].y,i+n);
       }
       else
       {
	      val[i+n]=a[i].b;
       	  link(a[i].x,i+n);
       	  link(a[i].y,i+n); 
	   }
	   if (findroot(1)!=findroot(n)) continue;
	   split(1,n);
	   ans=min(ans,mx[n]+a[i].a);
	  // cout<<ans<<endl;
   }
   if (ans==1e9) ans=-1;
   cout<<ans;
   return 0;
}

posted @ 2018-12-22 16:07  y_immortal  阅读(200)  评论(0编辑  收藏  举报