BZOJ4025 二分图 分治 并查集 二分图 并查集按秩合并 带权并查集

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

题目传送门 - BZOJ4025

题意

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

  $n\leq 10^5,m\leq 2\times 10^5,T\leq 10^5$

题解

  真是一道好题。

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

  先考虑按照时间二分。

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

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

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

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

  具体实现参见代码。

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

  首先考虑空间复杂度。

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

  考虑时间复杂度。

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

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

  

原文地址:https://www.cnblogs.com/zhouzhendong/p/8683831.html

时间: 2024-11-10 20:38:36

BZOJ4025 二分图 分治 并查集 二分图 并查集按秩合并 带权并查集的相关文章

POJ 1773 Parity game 带权并查集

分析:带权并查集,就是维护一堆关系 然后就是带权并查集的三步 1:首先确定权值数组,sum[i]代表父节点到子节点之间的1的个数(当然路径压缩后代表到根节点的个数) 1代表是奇数个,0代表偶数个 2:设计路径压缩算法 sum[x]=(sum[x]+sum[t])%2; 3:弄清合并根节点时的操作,小的在上: 注:这个题需要离散化 #include <stdio.h> #include <string.h> #include <algorithm> using names

「带权并查集」奇偶游戏

奇偶游戏 原题链接:奇偶游戏 题目大意 给你N个区间,每个区间给你它含1的奇偶性,问你哪些询问逻辑上冲突 题目题解 一道带权并查集的题,让我对带权并查集有了更深入的理解,带权并查集可以分为两种(在这道题中) "边带权"并查集 "扩展域"并查集 两种方法都是思维上的不同所造成的,其中第一种解法是最常见的,第二种解法在代码实现上是最简单的,我们先来对第一种进行探究 边带权,很明显,我们要在并查集的边上进行一个储存边权的操作,我们这里用d来表示当前节点到根节点的Xor值,

UVA - 10004 Bicoloring(判断二分图——交叉染色法 / 带权并查集)

d.给定一个图,判断是不是二分图. s.可以交叉染色,就是二分图:否则,不是. 另外,此题中的图是强连通图,即任意两点可达,从而dfs方法从一个点出发就能遍历整个图了. 如果不能保证从一个点出发可以遍历整个图,那么编程要注意了,应该从每个点出发遍历一次. s2.带权并查集来判断,略复杂.先略过.先上个博客:http://blog.csdn.net/zsc09_leaf/article/details/6727622 c.邻接矩阵,bfs #include<iostream> #include&

hdu 1829 &amp;amp;poj 2492 A Bug&amp;#39;s Life(推断二分图、带权并查集)

A Bug's Life Time Limit: 15000/5000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 8528    Accepted Submission(s): 2745 Problem Description Background  Professor Hopper is researching the sexual behavior of a rare

CodeForces - 687D: Dividing Kingdom II (二分图&amp;带权并查集)

Long time ago, there was a great kingdom and it was being ruled by The Great Arya and Pari The Great. These two had some problems about the numbers they like, so they decided to divide the great kingdom between themselves. The great kingdom consisted

Lightoj1009 Back to Underworld(带权并查集)

转载请注明出处: http://www.cnblogs.com/fraud/          ——by fraud Back to Underworld Time Limit:4000MS     Memory Limit:32768KB     64bit IO Format:%lld & %llu Description The Vampires and Lykans are fighting each other to death. The war has become so fierc

codeforces 687D Dividing Kingdom II 带权并查集(dsu)

题意:给你m条边,每条边有一个权值,每次询问只保留编号l到r的边,让你把这个图分成两部分 一个方案的耗费是当前符合条件的边的最大权值(符合条件的边指两段点都在一个部分),问你如何分,可以让耗费最小 分析:把当前l到r的边进行排序,从大到小,从大的开始不断加边,判断当前能否形成二分图,如果能形成二分图,继续加边 如果不能形成二分图,那当前边的权值就是最小耗费(是不是很眼熟) 思路很清晰,现在我们要解决的是如何判断可以形成二分图,有两种,一个是2染色当前图(肯定超时) 所以只剩一种方法,带权并查集

hdu3038(带权并查集)

题目链接: http://acm.split.hdu.edu.cn/showproblem.php?pid=3038 题意: n表示有一个长度为n的数组, 接下来有m行形如x, y, d的输入, 表示从第x,个元素到第y个元素的和为d(包括x, 和y), 问m行输入里面有几个是错误的(第一个输入是正确的); 思路: 很显然带权并查集咯,我们可以用距离的概念代替和的概念比较好理解一点,d表示x到y的和即x到y的距离; 可以用rank[x]表示x到其父亲节点的距离,  将正确的距离关系合并到并查集中

【POJ1182】 食物链 (带权并查集)

Description 动物王国中有三类动物A,B,C,这三类动物的食物链构成了有趣的环形.A吃B, B吃C,C吃A. 现有N个动物,以1-N编号.每个动物都是A,B,C中的一种,但是我们并不知道它到底是哪一种. 有人用两种说法对这N个动物所构成的食物链关系进行描述: 第一种说法是"1 X Y",表示X和Y是同类. 第二种说法是"2 X Y",表示X吃Y. 此人对N个动物,用上述两种说法,一句接一句地说出K句话,这K句话有的是真的,有的是假的.当一句话满足下列三条之