洛谷P2387 [NOI2014]魔法森林(LCT)

在XZY&XZZ巨佬的引领下,一枚蒟蒻终于啃动了这道题。。。。。。
这次还是第一次写LCT维护边权,还要化边为点,思路乱七八糟的,写起来也不顺手,还好调了许久终于AC啦。
贪心排序按一个关键字从小到大枚举边,维护另一个关键字的最小生成树,这样的思路真是太巧妙啦。(然而没有题解的滋养我什么也干不了)
只是关于写法上面,我又有些和Dalao们不一样的地方(因为是自己乱写的)。
link和cut好像脱离了传统意义,本蒟蒻用了link(x)表示将编号为x的边加入用LCT维护的最小生成树,cut(x)反之。
link其实是要连两次,因为要严格深度递增,就让边的一个端点的父亲指向这个代表边的点(轻边),再让代表边的点的父亲指向边的另一个端点(还是轻边)。当然啦,深度大的那个点要先makeroot,不然会影响正确性。
cut就是断两次了,把代表边的点下面那个点(深度大的)access,splay(代表边的点),此时两个端点一个深度小,在左边,一个深度大,在右边,左右儿子全断掉就OK。
其实常规link,cut没问题啊,我也不知道什么时候脑袋短路搞出这种不成熟的写法,不过稍微算算,实际操作量少了些,比起两遍link或cut常数会小一点。
update:这种link和cut可能存在问题。。。还是要写两遍(不知道当时怎么过的)
不过说到常数又留下一个败笔——这一题较特殊,维护连通性可以用并查集,避免了大量findroot(虽然不会T而已)。只是我没有写。findroot真慢呀!以后还是要记住这个小细节。
代码还是放一下

#include<cstdio>
#include<cstdlib>
#include<algorithm>
using namespace std;
#define R register int
#define I inline void
#define lc c[x][0]
#define rc c[x][1]
#define G ch=getchar()
#define min2(x,y) if(x>y)x=y;
const int N=200000,P=131072;
//P是用来区分点和边的,边的编号在LCT中不变,点的编号全部加上P(为压常用了这样一个数,加上位或运算)
int f[N],c[N][2],mx[N];
bool r[N];
I in(R&z){
	register char G;
	while(ch<'-')G;
	z=ch&15;G;
	while(ch>'-')z*=10,z+=ch&15,G;
}
struct EDGE{
	int u,v,a,b;
	inline bool operator<(EDGE x)const{
		return a<x.a;
	}
	I read(){
		in(u);in(v);in(a);in(b);
		u|=P;v|=P;//编号变化
	}
}e[N];//边放进结构体
inline bool nroot(R x){
	return c[f[x]][0]==x||c[f[x]][1]==x;
}
I pushup(R x){
	mx[x]=x;
	if(e[mx[x]].b<e[mx[lc]].b)mx[x]=mx[lc];
	if(e[mx[x]].b<e[mx[rc]].b)mx[x]=mx[rc];//三种情况都要看
}
I pushdown(R x){
	if(r[x]){
		R t=lc;lc=rc;rc=t;
		r[lc]^=1;r[rc]^=1;r[x]=0;
	}
}
I pushall(R x){//懒得手写栈,直接用函数堆栈(逃
	if(nroot(x))pushall(f[x]);
	pushdown(x);
}
I rotate(R x){
	R y=f[x],z=f[y],k=c[y][1]==x,w=c[x][!k];
	if(nroot(y))c[z][c[z][1]==y]=x;c[x][!k]=y;c[y][k]=w;
	f[w]=y;f[y]=x;f[x]=z;
	pushup(y);
}
I splay(R x){
	R y=x;
	pushall(x);
	while(nroot(x)){
		if(nroot(y=f[x]))
			rotate((c[y][0]==x)^(c[f[y]][0]==y)?x:y);
		rotate(x);
	}
	pushup(x);
}
I access(R x){
	for(R y=0;x;x=f[y=x])
		splay(x),rc=y,pushup(x);
}
I mroot(R x){
	access(x);splay(x);
	r[x]^=1;
}
inline int froot(R x){
	access(x);splay(x);
	while(lc)x=lc;
	return x;
}
I link(R x){
	R y=e[x].u,z=e[x].v;
	mroot(z);
	f[f[z]=x]=y;
}//非正常link与cut
I cut(R x){
	access(e[x].v);splay(x);
	lc=rc=f[lc]=f[rc]=0;
	pushup(x);
}
int main(){
	R n,m,i,y,z,ans=999999;//ans给极大值
	in(n);in(m);
	for(i=1;i<=m;++i)e[i].read();
	sort(e+1,e+m+1);
	for(i=1;i<=m;++i)
	{
		if((y=e[i].u)==(z=e[i].v))continue;
		mroot(y);
		if(y!=froot(z))link(i);//不联通,直接连
		else if(e[i].b<e[mx[z]].b)cut(mx[z]),link(i);//联通,但有更小的边替换
		mroot(1|P);
		if((1|P)==froot(n|P))min2(ans,e[i].a+e[mx[n|P]].b);//1与n联通就更新答案
	}
	printf("%d\n",ans==999999?-1:ans);
	return 0;
}
posted @ 2018-02-22 20:35  Flash_Hu  阅读(403)  评论(0编辑  收藏