• BZOJ3669 [Noi2014]魔法森林 LCT


    欢迎访问~原文出处——博客园-zhouzhendong

    去博客园看该题解


    题目传送门 - BZOJ3669


    题意概括

      有一个无向图,每条边分别有a、b两种权值。

      你要通过他,那么你自身的a、b两种权值必须得都不小于该边。

      现在你要从1走到n,问你自身的a+b最小为多少。


    题解

      我们可以按照a排序。

      然后依次加边。

      那么当前最大的a就是当前加入边的a。

      至于b,我们可以写LCT来维护。

      我们在加入一条边的时候,要判断当前的边连接的两个点是否连通。

      如果不连通,那么直接加入此边。

      如果连通,那么要找出两点之间的树链的最大边权b,然后在当前边和那条边中选择b小的留下。

      每次更新答案,只需要判断1和n是否连通然后求一下值就可以了,应该是很简单的。

      至于听的一头雾水的同学们,如何维护边??那么,建议你看看这个↓

      BZOJ2594 [Wc2006]水管局长数据加强版 LCT kruskal


    代码

    #include <cstring>
    #include <cstdio>
    #include <algorithm>
    #include <cmath>
    #include <cstdlib>
    using namespace std;
    const int N=50005,M=100005,S=N+M;
    int n,m;
    int fa[S],son[S][2],rev[S],Max[S],val[S];
    struct Edge{
    	int x,y,a,b;
    	void read(){
    		scanf("%d%d%d%d",&x,&y,&a,&b);
    	}
    }e[M];
    void clear(int x,int v){
    	fa[x]=son[x][0]=son[x][1]=rev[x]=0;
    	Max[x]=val[x]=v;
    }
    bool isroot(int x){
    	return son[fa[x]][1]!=x&&son[fa[x]][0]!=x;
    }
    void pushup(int x){
    	int ls=son[x][0],rs=son[x][1];
    	Max[x]=e[Max[ls]].b>e[Max[rs]].b?Max[ls]:Max[rs];
    	Max[x]=e[val[x]].b>e[Max[x]].b?val[x]:Max[x];
    }
    void pushdown(int x){
    	if (rev[x]){
    		rev[x]=0;
    		rev[son[x][0]]^=1;
    		rev[son[x][1]]^=1;
    		swap(son[x][0],son[x][1]);
    	}
    }
    void pushadd(int x){
    	if (!isroot(x))
    		pushadd(fa[x]);
    	pushdown(x);
    }
    int wson(int x){
    	return son[fa[x]][1]==x;
    }
    void rotate(int x){
    	if (isroot(x))
    		return;
    	int y=fa[x],z=fa[y],L=wson(x),R=L^1;
    	if (!isroot(y))
    		son[z][wson(y)]=x;
    	fa[x]=z,fa[y]=x,fa[son[x][R]]=y;
    	son[y][L]=son[x][R],son[x][R]=y;
    	pushup(y),pushup(x);
    }
    void splay(int x){
    	pushadd(x);
    	for (int y=fa[x];!isroot(x);rotate(x),y=fa[x])
    		if (!isroot(y))
    			rotate(wson(x)==wson(y)?y:x);
    }
    void access(int x){
    	for (int t=0;x;t=x,x=fa[x]){
    		splay(x);
    		son[x][1]=t;
    		pushup(x);
    	}
    }
    void rever(int x){
    	access(x);
    	splay(x);
    	rev[x]^=1;
    }
    void link(int x,int y){
    	rever(x);
    	fa[x]=y;
    }
    void split(int x,int y){
    	rever(y);
    	access(x);
    	splay(x);
    }
    void cut(int x,int y){
    	split(x,y);
    	fa[y]=son[x][0]=0;
    }
    int find(int x){
    	access(x);
    	splay(x);
    	while (son[x][0])
    		x=son[x][0];
    	return x;
    }
    bool cmp(Edge a,Edge b){
    	return a.a<b.a;
    }
    int main(){
    	scanf("%d%d",&n,&m);
    	for (int i=1;i<=m;i++)
    		e[i].read();
    	sort(e+1,e+m+1,cmp);
    	e[0].b=0;
    	for (int i=1;i<=n+m;i++)
    		clear(i,0);
    	int ans=2333333;
    	for (int i=1;i<=m;i++){
    		int a=e[i].a,b=e[i].b,x=e[i].x,y=e[i].y;
    		if (x==y)
    			continue;
    		bool flag=1;
    		if (find(x)==find(y)){
    			split(x,y);
    			int ce=Max[x];
    			if (e[ce].b>b){
    				cut(e[ce].x,ce+n);
    				cut(e[ce].y,ce+n);
    			}
    			else 
    				flag=0;
    		}
    		if (flag){
    			clear(i+n,i);
    			link(x,i+n);
    			link(y,i+n);
    		}
    		if (find(1)==find(n)){
    			split(1,n);
    			int ne=Max[1];
    			ans=min(ans,a+e[ne].b);
    		}
    	}
    	printf("%d",ans==2333333?-1:ans);
    	return 0;
    }
    

      

  • 相关阅读:
    全栈必备Linux 基础
    Linux 的 Socket IO 模型
    Vim
    Linux 下使用 Sar 简介
    提高效率,推荐 5 款命令行工具
    Vim小技巧
    剑指Offer 矩形覆盖
    剑指Offer 变态跳台阶
    剑指Offer 跳台阶
    2016 网易校招内推C/C++第二场8.6
  • 原文地址:https://www.cnblogs.com/zhouzhendong/p/BZOJ3669.html
Copyright © 2020-2023  润新知