• 洛谷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
    ",ans==999999?-1:ans);
    	return 0;
    }
    
  • 相关阅读:
    B树和B+树的区别
    宏(腾讯)
    数组作为函数参数传递时退化为指针(腾讯)
    求已知表达式的后缀形式(腾讯)
    JAVA笔记12-接口interface
    JAVA笔记11-Final关键字
    JAVA笔记10-抽象类
    JAVA笔记9-多态(动态绑定、池绑定)
    JAVA笔记8-对象转型casting
    JAVA笔记7-Object类之toString方法和equals方法
  • 原文地址:https://www.cnblogs.com/flashhu/p/8460043.html
Copyright © 2020-2023  润新知