zoukankan      html  css  js  c++  java
  • 题解[CF891C Envy]

    题目链接

    题意:给定一张无向连通图,每次给出一些边,询问这些边能否同时在一个最小生成树中出现。

    先建出任意一个最小生成树,对树上与非树上的边讨论。

    首先,如果询问的边会构成一个环,那就完全不可能在同一最小生成树中。

    其次,考虑一条非树边 ((x,y,w)) 什么时候能替换一条在原先最小生成树上的边。

    那就必须满足 (x,y) 两个点在原先最小生成树路径上的边的最大值 (geq) (w) ,不然不可行。

    那这时候这条边就能将这条边权最大的边替换掉。

    但如果这条边权最大的边被要求了必须出现,就需要另外的边去替换。

    由于有动态的替换与求路径最大值,可以考虑 ( ext{LCT}) 化边为点实现。

    先将询问中在原最小生成树上的边的权值设为 (0) 保证不会被替换。

    再对非树边查路径最大值看能否替换,并记录下被替换过的边,在最终要复原回去。

    这样的时间复杂度是 (O(nlog n)) 的(默认 (n,m) 同阶)。

    但有于 ( ext{LCT}) 的巨大常数,且为了查路径最值的化边为点,点数已去到 (1e6) 级别。

    所以这种方法特别卡常,顺手最劣解

    最终代码:

    #pragma GCC optimize("Ofast")
    #include<bits/stdc++.h>
    using namespace std;
    const int N=1e6+10;
    int n,m,q,x,y,v,k,ax,ay,tot;bool flag,bb[N];
    int w[N],ww[N],xx[N],yy[N],a[N],f[N],id[N];
    inline int getf(int x){while(x^f[x])x=f[x]=f[f[x]];return x;}
    struct pre_state{int x,y;}ps[N<<1];
    namespace FastIO
    {//此处省略带缓存的 FastIO
    }
    using namespace FastIO;
    struct lct{
    	int mxid[N],son[N][2],tmpid,anc[N],rev[N],i,y,_y,_x,_id,rx,ry,mxy;bool b;
    	inline bool nroot(int x){return son[anc[x]][0]==x||son[anc[x]][1]==x;}
    	inline bool p(int x){return son[anc[x]][1]==x;}
    	inline void rev_(int x){rev[x]^=1;son[x][0]^=son[x][1]^=son[x][0]^=son[x][1];}
    	inline void fix(int x){
    		mxid[x]=x;
    		if(w[tmpid=mxid[son[x][0]]]>w[x])mxid[x]=tmpid;
    		if(w[tmpid=mxid[son[x][1]]]>w[mxid[x]])mxid[x]=tmpid;
    	}
    	inline void pushdown(int x){
    		if(rev[x]){if(son[x][0])rev_(son[x][0]);if(son[x][1])rev_(son[x][1]);rev[x]=0;}
    	}
    	void pushall(int x){if(nroot(x))pushall(anc[x]);pushdown(x);}
    	inline void rotate(int x){
    		y=anc[x];_x=anc[y];b=p(x);
    		if(nroot(y))son[_x][p(y)]=x;
    		anc[x]=_x;
    		anc[son[y][b]=son[x][!b]]=y;
    		anc[son[x][!b]=y]=x;
    		fix(y);fix(x);fix(_x);
    	}
    	inline void splay(int x){
    		pushall(x);for(;i=anc[x],nroot(x);rotate(x))if(nroot(i))rotate(p(x)==p(i)?i:x);
    	}
    	inline void access(int x){
    		for(_y=0;x;_y=x,x=anc[x])splay(x),son[x][1]=_y,fix(x);
    	}
    	inline void makeroot(int x){access(x);splay(x);rev_(x);}
    	inline void split(int x,int y){makeroot(x);access(y);splay(y);}
    	inline void link(int x,int y){makeroot(x);anc[x]=y;}
    	inline void cut(int x,int y){split(x,y);son[y][0]=anc[x]=0;fix(y);}
    	inline void link(int x,int y,int id){
    		split(x,y);mxy=mxid[y];
    		if(w[mxy]>=w[id]){
    			_id=mxy;
    			cut(xx[_id],_id);cut(_id,yy[_id]);
    			link(x,id);link(id,y);
    			ps[++tot]=(pre_state){_id,id};
    		}
    		else flag=1;
    	}
    	inline void recover(){
    		for(;tot;--tot){
    			ry=ps[tot].y,rx=ps[tot].x;
    			cut(xx[ry],ry);cut(yy[ry],ry);
    			link(xx[rx],rx);link(yy[rx],rx);
    		}
    	}
    	void getbb(){for(int i=1;i<=m;++i)split(xx[i],i),bb[i]=son[i][0]==xx[i];}
    }L;
    bool cmp(int a,int b){return w[a]<w[b];}
    main(){
    	qin>>n>>m;register int i;
    	for(i=1;i<=m;++i)qin>>xx[i]>>yy[i]>>w[i];
    	for(i=1;i<=m;++i)xx[i]+=m,yy[i]+=m,ww[i]=w[i],id[i]=i;
    	for(i=1;i<=n;++i)f[i]=i;
    	sort(id+1,id+m+1,cmp);
    	for(i=1;i<=m;++i){
    		x=xx[id[i]]-m,y=yy[id[i]]-m;
    		ax=getf(x),ay=getf(y);
    		if(ax==ay)continue;
    		f[ax]=ay;
    		L.link(x+m,id[i]);L.link(y+m,id[i]);
    	}//老老实实打kruskal
    	L.getbb();tot=0;qin>>q;
    	for(i=1;i<=n;++i)f[i]=i;
    	while(q--){
    		qin>>k;flag=1;
    		for(i=1;i<=k;++i)qin>>a[i],flag&=bb[a[i]];
    		if(flag){puts("YES");continue;}//全部在原MST的边显然可行
    		flag=0;
    		for(i=1;i<=k;++i){
    			ax=getf(xx[a[i]]-m);
    			ay=getf(yy[a[i]]-m);
    			if(ax==ay)flag=1;
    			else f[ax]=ay;
    		}//并查集判环
    		for(i=1;i<=k;++i)x=xx[a[i]]-m,f[x]=x,y=yy[a[i]]-m,f[y]=y;
    		if(flag){puts("NO");continue;}
    		for(i=1;i<=k;++i)if(bb[a[i]])w[a[i]]=0,L.splay(a[i]);
    		for(i=1;i<=k;++i)if(!bb[a[i]]){
    			L.link(xx[a[i]],yy[a[i]],a[i]);
    			if(flag)break;
    		}
    		for(i=1;i<=k;++i)if(bb[a[i]])w[a[i]]=ww[a[i]],L.splay(a[i]);
    		L.recover();//复原
    		if(flag)puts("NO");
    		else puts("YES");
    	}
    }
    
  • 相关阅读:
    SLAM+语音机器人DIY系列:(二)ROS入门——3.在ubuntu16.04中安装ROS kinetic
    SLAM+语音机器人DIY系列:(二)ROS入门——2.ROS系统整体架构
    2017年 年计划
    125. Valid Palindrome
    一道多树递归面试题
    顺序表查找和有序表查找
    c++中常见概念、关键字等的区别
    两个栈实现一个队列,两个队列实现一个栈
    150. Evaluate Reverse Polish Notation
    堆排序
  • 原文地址:https://www.cnblogs.com/Y-B-X/p/15424366.html
Copyright © 2011-2022 走看看