zoukankan      html  css  js  c++  java
  • 【CF576E】Painting Edges(线段树分治+并查集)

    点此看题面

    • 给定一张(n)个点(m)条边的图,有(k)种颜色。
    • (q)次操作,每次修改一条边的颜色,如果修改之后这种颜色的边会形成奇环就不执行该操作。
    • 问每次操作是否会执行。
    • (n,m,qle5 imes10^5,kle50)

    线段树分治

    刚看完题面以为是道大裸题,仔细一想突然发现因为一些操作有可能不执行,无法直接确定出每条边被删除的时间。

    但实际上,一次操作如果不执行,我们可以看作它给这条边染上了与原先相同的颜色。

    然后我们又发现线段树分治有一个很好的性质,就是我们肯定会先访问到一个操作对应的叶节点,然后才会处理到这个操作的染色修改。

    即,我们每次都可以先判断这个操作是否会执行,这样就能确定它到底染了什么颜色。

    于是发现这还是一道大裸题。。。

    按秩合并并查集判奇环

    还有一个很简单的问题,就是按秩合并并查集怎么判奇环。

    (V(x))表示点(x)到它所在连通块的根的路径长度的奇偶性。

    比如说当前要在(x,y)之间连边,它们所在连通块的根分别是(fx,fy)

    那么(y)所在连通块的所有点,到新的根节点(fx)路径长度的奇偶性都会异或上(V(x) xor V(y) xor 1)

    那么只要对于并查集每个点打一个标记,然后一个点的(V)就是它到根所有标记的异或和。

    代码:(O(nlog^2n))

    #include<bits/stdc++.h>
    #define Tp template<typename Ty>
    #define Ts template<typename Ty,typename... Ar>
    #define Reg register
    #define RI Reg int
    #define Con const
    #define CI Con int&
    #define I inline
    #define W while
    #define N 500000
    #define K 50
    using namespace std;
    int n,m,k,Qt,lst[N+5];struct line {int x,y;}s[N+5];struct Q {int x,c;}q[N+5];
    class FastIO
    {
    	private:
    		#define FS 100000
    		#define tc() (A==B&&(B=(A=FI)+fread(FI,1,FS,stdin),A==B)?EOF:*A++)
    		#define D isdigit(c=tc())
    		char c,*A,*B,FI[FS];
    	public:
    		I FastIO() {A=B=FI;}
    		Tp I void read(Ty& x) {x=0;W(!D);W(x=(x<<3)+(x<<1)+(c&15),D);}
    		Ts I void read(Ty& x,Ar&... y) {read(x),read(y...);}
    }F;
    class UnionFindSet//按秩合并并查集
    {
    	private:
    		int f[N+5],g[N+5],T,Sx[N+5],Sy[N+5];bool tg[N+5],Sg[N+5];
    	public:
    		I int fa(CI x) {return f[x]?fa(f[x]):x;}
    		I int V(CI x) {return (f[x]?V(f[x]):0)^tg[x];}//当前点到根所有标记异或和
    		I void U(RI x,RI y)//合并
    		{
    			RI fx=fa(x),fy=fa(y);if(++T,fx==fy) return (void)(Sx[T]=-1);
    			g[fx]<g[fy]&&(swap(x,y),swap(fx,fy),0),Sx[T]=fx,Sy[T]=fy,
    			g[fx]+=(Sg[T]=g[fx]==g[fy]),f[fy]=fx,tg[fy]=V(x)^V(y)^1;
    		}
    		I void Back() {~Sx[T]&&(g[Sx[T]]-=Sg[T],f[Sy[T]]=tg[Sy[T]]=0),--T;}//撤销
    }U[K+5];
    class SegmentTreeSolver//线段树分治
    {
    	private:
    		#define PT CI l=1,CI r=Qt,CI rt=1
    		#define LT l,mid,rt<<1
    		#define RT mid+1,r,rt<<1|1
    		int o[N+5];vector<int> P[N<<2];vector<int>::iterator it;
    	public:
    		I void T(CI L,CI R,CI p,PT)//给区间[L,R]加入操作p
    		{
    			if(L<=l&&r<=R) return P[rt].push_back(p);RI mid=l+r>>1;
    			L<=mid&&(T(L,R,p,LT),0),R>mid&&(T(L,R,p,RT),0);
    		}
    		I void Solve(PT)//线段树分治
    		{
    			for(it=P[rt].begin();it!=P[rt].end();++it) U[q[*it].c].U(s[q[*it].x].x,s[q[*it].x].y);//执行操作
    			RI p,x,y,c;if(l==r) x=s[p=q[l].x].x,y=s[p].y,c=q[l].c,//对于叶节点
    				puts(U[c].fa(x)^U[c].fa(y)||U[c].V(x)^U[c].V(y)?(o[p]=c,"YES"):(q[l].c=o[p],"NO"));//判断操作是否要执行
    			else {RI mid=l+r>>1;Solve(LT),Solve(RT);}//对于非叶节点直接递归
    			for(it=P[rt].begin();it!=P[rt].end();++it) U[q[*it].c].Back();//撤销操作
    		}
    }S;
    int main()
    {
    	RI i;for(F.read(n,m,k,Qt),i=1;i<=m;++i) F.read(s[i].x,s[i].y);
    	for(i=1;i<=Qt;++i) F.read(q[i].x,q[i].c),
    		lst[q[i].x]&&(S.T(lst[q[i].x]+1,i,lst[q[i].x]),0),lst[q[i].x]=i;//求出每一个操作对应范围
    	for(i=1;i<=m;++i) lst[i]&&(S.T(lst[i]+1,Qt,lst[i]),0);return S.Solve(),0;
    }
    
  • 相关阅读:
    Oracle与MySQL的转化差异
    iOS 创建静态库文件时去掉当中的Symbols
    hdu4336 Card Collector 状态压缩dp
    随机森林——Random Forests
    OpenCV码源笔记——Decision Tree决策树
    海明距离hamming distance
    学习OpenCV——Surf简化版
    学习OpenCV——用OpenCv画漫画
    学习OpenCV——ORB简化版&Location加速版
    学习OpenCV——hand tracking手势跟踪
  • 原文地址:https://www.cnblogs.com/chenxiaoran666/p/CF576E.html
Copyright © 2011-2022 走看看