zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    Filebeat
    kafka 分区 spark excutor task rdd
    Java 方法重写方法重载
    Spark union
    Storm
    pbuilder编译构建工具分析
    TCP的拥塞控制 (四)
    TCP的拥塞控制 (三)
    TCP的拥塞控制 (二)
    TCP的拥塞控制 (一)
  • 原文地址:https://www.cnblogs.com/youth518/p/14111469.html
Copyright © 2011-2022 走看看