zoukankan      html  css  js  c++  java
  • BZOJ4025 二分图 分治 并查集 二分图 带权并查集按秩合并

    原文链接http://www.cnblogs.com/zhouzhendong/p/8683831.html

    题目传送门 - BZOJ4025

    题意

      有$n$个点,有$m$条边。有$T$个时间段。其中第$i$条边连接节点$x_i,y_i$,并且在$start_i$时刻出现,在$end_i$时刻消失。问每一个时刻的图是不是二分图。

      $nleq 10^5,mleq 2 imes 10^5,Tleq 10^5$

    题解

      真是一道好题。

      做这题我才发现我从来没写过按秩合并的并查集QAQ。

      先考虑按照时间二分。

      对于某一段时间,我们可以把所有在当前时间段一直出现的边连起来。这个可以用按秩合并的带权并查集维护。(注意子程序退出的时候要撤销所有操作)

      如果在加边的过程中,发现冲突,那么该区间全部都是NO了。

      否则把除了完全覆盖当前区间的边之外的,对左区间有关的扔到左边,对右区间有关的扔到右边。然后递归子区间处理。

      注意区间长度为1的时候不要再递归下去了,会RE的。

      具体实现参见代码。

      我们来分析一下为什么复杂度是对的。

      首先考虑空间复杂度。

      考虑每一层递归的时候最多有$O(n)$条边,最多有$O(log n)$层,所以空间复杂度为$O(nlog n)$。

      考虑时间复杂度。

      我们发现主要的复杂度在边的处理上。一条边在多少个时间段被连接,就是他对总时间复杂度的贡献。

      显然每一条边在同一个区间只会被连接一下,但是要按秩合并并查集,所以单次复杂度为$O(log n)$。考虑到一个边最多在$O(log n)$个区间被连接(和线段树区间覆盖的原理差不多吧)。所以每一条边最多贡献$O(log^2 n)$的时间复杂度。所以有$m$条边,显然$m$的上限和$n$同阶,当他是$n$就可以了,所以总的时间复杂度为$O(nlog^2 n)$。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    const int N=200005;
    struct Edge{
    	int x,y,s,t;
    	void get(){
    		scanf("%d%d%d%d",&x,&y,&s,&t),s++;
    	}
    }e[N];
    int n,m,T,ans[N];
    vector <int> x;
    struct UFset{
    	int n,fa[N],depth[N],d[N],stack[N],top;
    	void init(int _n){
    		n=_n;
    		for (int i=1;i<=n;i++)
    			fa[i]=i;
    		memset(depth,0,sizeof depth);
    		memset(d,0,sizeof d);
    		top=0;
    	}
    	int getf(int x){
    		while (fa[x]!=x)
    			x=fa[x];
    		return x;
    	}
    	int getdis(int x){
    		int ans=0;
    		while (fa[x]!=x)
    			ans^=d[x],x=fa[x];
    		return ans;
    	}
    	bool Merge(int x,int y){
    		int D=getdis(x)^getdis(y)^1;
    		x=getf(x),y=getf(y);
    		if (x==y)
    			return D==0;
    		if (depth[x]<depth[y])
    			swap(x,y);
    		if (depth[x]==depth[y])
    			depth[x]++,stack[++top]=-x;
    		fa[y]=x,d[y]=D,stack[++top]=y;
    		return 1;
    	}
    	void Split(int time){
    		while (top>time){
    			int x=stack[top--];
    			if (x<0)
    				depth[-x]--;
    			else
    				fa[x]=x,d[x]=0;
    		}
    	}
    }s;
    void solve(int L,int R,vector <int> &now){
    	if (now.size()==0)
    		return;
    	vector <int> Lpart,Rpart;
    	Lpart.clear(),Rpart.clear();
    	int time=s.top,mid=(L+R)>>1;
    	for (int i=0;i<now.size();i++){
    		int id=now[i];
    		if (e[id].s<=L&&e[id].t>=R){
    			if (!s.Merge(e[id].x,e[id].y)){
    				for (int j=L;j<=R;j++)
    					ans[j]=0;
    				s.Split(time);
    				return;
    			}
    		}
    		else {
    			if (e[id].t<=mid)
    				Lpart.push_back(id);
    			else if (e[id].s>mid)
    				Rpart.push_back(id);
    			else if (e[id].s<=mid&&e[id].t>mid)
    				Lpart.push_back(id),Rpart.push_back(id);
    		}
    	}
    	if (L==R){
    		s.Split(time);
    		return;
    	}
    	solve(L,mid,Lpart);
    	solve(mid+1,R,Rpart);
    	s.Split(time);
    }
    int main(){
    	scanf("%d%d%d",&n,&m,&T);
    	x.clear();
    	for (int i=1;i<=m;i++)
    		e[i].get(),x.push_back(i);
    	for (int i=1;i<=T;i++)
    		ans[i]=1;
    	s.init(n);
    	solve(1,T,x);
    	for (int i=1;i<=T;i++)
    		puts(ans[i]?"Yes":"No");
    	return 0;
    }
    

      

  • 相关阅读:
    BZOJ 5308 [ZJOI2018] Day2T2 胖 | 二分 ST表
    CodeForces 464E The Classic Problem | 呆克斯歘 主席树维护高精度
    BZOJ5298 [CQOI2018] 交错序列 | 矩阵乘法和一个trick
    # BZOJ5300 [CQOI2018]九连环 题解 | 高精度 FFT
    [BZOJ5248] 2018九省联考 D1T1 一双木棋 | 博弈论 状压DP
    【2018九省联考】后的瞎扯
    BZOJ 4671 异或图 | 线性基 容斥 DFS
    Luogu 4294 [WC2008]游览计划 | 斯坦纳树
    BZOJ 2434 阿狸的打字机 | AC自动机
    UOJ#7. 【NOI2014】购票 | 线段树 凸包优化DP
  • 原文地址:https://www.cnblogs.com/zhouzhendong/p/BZOJ4025.html
Copyright © 2011-2022 走看看