BZOJ4025 二分图
? 解题记录 ? ? BZOJ ? ? 并查集 ? ? 分治 ? ? 线段树分治 ?    2018-05-21 18:10:14    738    0    0

【题目描述】

神犇有一个n个节点的图。因为神犇是神犇,所以在T时间内一些边会出现后消失。神犇要求出每一时间段内这个图是否是二分图。这么简单的问题神犇当然会做了,于是他想考考你。

 

【输入】

输入数据的第一行是三个整数n,m,T。

第2行到第m+1行,每行4个整数u,v,start,end。第i+1行的四个整数表示第i条边连接u,v两个点,这条边在start时刻出现,在第end时刻消失。

 

 

【输出】

输出包含T行。在第i行中,如果第i时间段内这个图是二分图,那么输出“Yes”,否则输出“No”,不含引号。

 

 

【输入样例】

3 3 3
1 2 0 2
2 3 0 3
1 3 1 2

【输出样例】

Yes
No
Yes

【提示】

样例说明:

0时刻,出现两条边1-2和2-3。

第1时间段内,这个图是二分图,输出Yes。

1时刻,出现一条边1-3。

第2时间段内,这个图不是二分图,输出No。

2时刻,1-2和1-3两条边消失。

第3时间段内,只有一条边2-3,这个图是二分图,输出Yes。

 

数据范围:

n≤100000,m≤200000,T≤100000,1≤u,v≤n,0≤start≤end≤T。

本题虽然可以用LCT做,但也可以用一种十分特殊的分治技巧。

我们考虑在时间轴上做分治。对于一条边,我们考虑在时间区间插入这条边。对于区间插入我们可以考虑模仿线段树的做法——对时间轴建立线段树,类似标记永久化的,把一条边出现的时段分配到线段树上log个区间,在线段树上进行区间修改操作。等到把所有的边都分配好之后我们dfs整个线段树,用并查集维护环的奇偶性,每到一个节点把该节点所有的边插入并查集,每出一个节点撤销该节点进行的所有操作即可。(具体做法见代码)

其实实现起来的话我们可以一边分区间一边dfs——每到一层遍历当前时间区间分到的所有的边,把左右区间恰好为当前管辖区间的边插入并查集,把其他操作用线段树的做法递归到左边和右边(分r <= mid, l > mid, l <=mid < r三种情况),这一步可以新开一个vector,把vector数组下标分配下去。这样我们写一个带撤销的并查集就好了。

#include<cstdio>
#include<iostream>
#include<cstring>
#include<algorithm>
#include<stack>
#include<vector>
#define For(i, a, b) for(register int i = a; i <= b; ++i)
using namespace std;
const int maxn = 2e5 + 5;

namespace DSU {
	stack<int > stk;
	stack<int > dstk;
	int fa[maxn], dis[maxn], siz[maxn], tmp;
	void init(int n) {For(i, 1, n) fa[i] = i, siz[i] = 1;}
	int find(int x) {return fa[x] == x ? x : find(fa[x]);}
	int Gdis(int x) {return fa[x] == x ? 0 : dis[x] ^ Gdis(fa[x]);}
	bool combine(int x, int y) {
		int w = Gdis(x) ^ Gdis(y) ^ 1;
		x = find(x), y = find(y);
		if(x == y) return false;
		if(siz[y] > siz[x]) swap(x, y); 
		dstk.push(dis[y]), dis[y] = w, fa[y] = x;
		stk.push(y), siz[x] += siz[y];
		return true;
	}
	void remove() {
		tmp = stk.top();
		dis[tmp] = dstk.top();
		siz[fa[tmp]] -= siz[tmp];
		fa[tmp] = tmp, stk.pop(), dstk.pop();
	}
	void back(int x) {while(x--) remove();}
}

int cnt, ans[maxn], n, m, t;
struct OPR {
	int u, v, l, r;
	OPR CH(int a, int b) {return (OPR) {u, v, a, b};}
}npr;
vector<OPR > NP[maxn << 1];
void cdq(int l, int r, int now) {
	int mid = l + r >> 1, CL = ++cnt, CR = ++cnt, oprs = 0;
	if(l == r) {
		for(register int i = NP[now].size() - 1; i >= 0; --i) {
			npr = NP[now][i];
			if(DSU::combine(npr.u, npr.v)) ++oprs; else
			if(DSU::Gdis(npr.u) ^ DSU::Gdis(npr.v) ^ 1)
				return DSU::back(oprs), ans[l] = -1, void();
		}
		return DSU::back(oprs), ans[l] = 1, void();
	}
	for(register int i = NP[now].size() - 1; i >= 0; --i) {
		npr = NP[now][i];
		if(npr.l == l && npr.r == r) {
			if(DSU::combine(npr.u, npr.v)) ++oprs; else
			if(DSU::Gdis(npr.u) ^ DSU::Gdis(npr.v) ^ 1) {
				For(j, l, r) ans[j] = -1;
				return DSU::back(oprs), void();
			}
		} else {
			if(npr.r <= mid) NP[CL].push_back(npr);
			else if(npr.l > mid) NP[CR].push_back(npr);
			else {
				NP[CL].push_back(npr.CH(npr.l, mid));
				NP[CR].push_back(npr.CH(mid + 1, npr.r));
			}
		}
	}
	cdq(l, mid, CL), cdq(mid + 1, r, CR);
	DSU::back(oprs);
}

int main() {
	scanf("%d%d%d", &n, &m, &t), DSU::init(n);
	For(i, 1, m) {
		scanf("%d%d%d%d", &npr.u, &npr.v, &npr.l, &npr.r);
		--npr.r, NP[cnt].push_back(npr);
	}
	cdq(0, t, 0);
	For(i, 0, t - 1) printf("%s\n", ans[i] > 0 ? "Yes" : "No");
	return 0;
} 

上一篇: BZOJ2001 [Hnoi2010]City 城市建设

下一篇: 洛谷P3642 [APIO2016]烟火表演

738 人读过
立即登录, 发表评论.
没有帐号? 立即注册
0 条评论
文档导航