其他分享
首页 > 其他分享> > 【洛谷P5787】二分图 /【模板】线段树分治

【洛谷P5787】二分图 /【模板】线段树分治

作者:互联网

题目

题目链接:https://www.luogu.com.cn/problem/P5787
神犇有一个 \(n\) 个节点的图。
因为神犇是神犇,所以在 \(k\) 时间内有 \(m\) 条边会出现后消失。
神犇要求出每一时间段内这个图是否是二分图。
这么简单的问题神犇当然会做了,于是他想考考你。
\(n,k=10^5,m=2\times 10^5\)。

思路

建立一棵线段树,把每一个操作的区间 \([l,r]\) 在线段树上找到,并在每一个区间的 vector 中加入连接 \(x,y\) 两点的操作。
然后深度优先搜索整棵线段树,搜索到一个区间 \([l,r]\) 时,把这个区间的所有操作执行,具体的,为了判断是不是二分图,我们可以采用扩展域并查集,把每一个点拆成两个点,然后类比黑白染色,黑点连白点,白点连黑点。如果某个时刻存在一个点的黑点和白点在同一集合内,说明存在奇环,\([l,r]\) 均不是二分图。
否则继续分治,如果到达叶子时依然没有奇环,那么就是二分图。
回溯的时候需要撤销并查集,考虑采用不路径压缩,只按秩合并的并查集即可。
估计只有我一个人按秩合并一直以为是按照大小吧。其实是按照深度。
时间复杂度 \(O(k\log k\log m)\)。

代码

#include <bits/stdc++.h>
#define mp make_pair
#define ST first
#define ND second
using namespace std;

const int N=200010;
int n,m,t,father[N*2],dep[N*2];
stack<int> st;

int find(int x)
{
	return x==father[x]?x:find(father[x]);
}

bool merge(int x,int y)
{
	x=find(x); y=find(y);
	if (x==y) return 1;
	if (dep[x]<dep[y]) swap(x,y);
	father[y]=x; dep[x]+=(dep[x]==dep[y]);
	st.push(y);
	if (find(x)==find(x+n) || find(y)==find(y+n)) return 0;
	return 1;
}

struct SegTree
{ 
	vector<pair<int,int> > e[N*4];
	
	void update(int x,int l,int r,int ql,int qr,int p,int q)
	{
		if (ql<=l && qr>=r)
		{
			e[x].push_back(mp(p,q));
			return;
		}
		int mid=(l+r)>>1;
		if (ql<=mid) update(x*2,l,mid,ql,qr,p,q);
		if (qr>mid) update(x*2+1,mid+1,r,ql,qr,p,q);
	}
	
	void solve(int x,int l,int r)
	{
		int cnt=st.size(); bool flag=1;
		for (int i=0;i<e[x].size();i++)
		{
			int p=e[x][i].ST,q=e[x][i].ND;
			if (!merge(p,q+n)) { flag=0; break; }
			if (!merge(p+n,q)) { flag=0; break; }
		}
		if (!flag)
			for (int i=l;i<=r;i++) printf("No\n");
		else if (l<r)
		{
			int mid=(l+r)>>1;
			solve(x*2,l,mid); solve(x*2+1,mid+1,r);
		}
		else printf("Yes\n");
		for (;st.size()>cnt;st.pop())
		{
			int x=st.top();
			dep[father[x]]-=(dep[father[x]]==dep[x]); father[x]=x;
		}
	}
}seg;

int main()
{
	scanf("%d%d%d",&n,&m,&t);
	for (int i=1,x,y,l,r;i<=m;i++)
	{
		scanf("%d%d%d%d",&x,&y,&l,&r);
		seg.update(1,1,t,l+1,r,x,y);
	}
	for (int i=1;i<=n*2;i++)
		father[i]=i;
	seg.solve(1,1,n);
	return 0;
}

标签:二分,P5787,洛谷,int,father,st,dep,模板,神犇
来源: https://www.cnblogs.com/stoorz/p/14402511.html