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

pengchujie / 2023-08-26 / 原文

传送门

分析:

\(1\)并查集判断二分图:

定义\(2n\)个点,染成黑白两色,代表两个不同的集合,\(a_1\)\(a_{1+n}\)为不同的颜色,以此类推,对于\(a_i\)\(a_j\)的连边,判断\(a_i\)\(a_j\)是否属于同一集合,若属于,则该图不是二分图;否则\(a_i\)的并查集和\(a_{j+n}\)的并查集合并,\(a_j\)的并查集和\(a_{i+n}\)的并查集合并。

证明正确性:

对于\(i,j\leq n\),若\(a_i\)\(a_j\)属于同一个集合,则从\(a_i\)\(a_j\)的边数必然为偶数个,因为每到一个小于\(n\)的点必然会经过一个大于\(n\)的点(我们是这么构造的,所有小于\(n\)的点都只与大于\(n\)的连边),因为\(i,j\leq n\),所以\(a_i\)\(a_j\)的路径中(不包括\(a_i,a_j\))有\(k\)个大于\(n\)的点,有\(k-1\)个小于\(n\)的点,共有\(2k\)条边,满足二分图的定义:二分图中不存在长度为奇数的环。

再将\(a_i\)\(a_j\)为同一并查集解释为\(a_i\)\(a_j\)为在同一集合,\(a_i\)\(a_{j+n}\)为同一并查集解释为\(a_i\)\(a_j\)不在同一集合即可。

证毕。

\(2\)线段树分治

核心思想:离线在时间轴上建立线段树,对于每个操作,定义为线段树内的区间操作,然后遍历线段树输出答案

目的:在低时间复杂度内解决一类在线算法并不优秀的问题

这个不好说思路,在代码中体现

\(3\)可撤销并查集

因为每条边只存在一定时间,所以并查集要支持回溯,所以不能用路径压缩,要用按秩合并

上代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const ll N=4e6+50;
ll n,m,k,top;
struct E{ll x,y;}e[N];
struct jgt{ll x,y,add;}st[N];
vector<ll> t[N];
ll fa[N],h[N];
ll find(ll x)
{
	while(x!=fa[x]) x=fa[x];
	return fa[x];
}
void add(ll wz,ll l,ll r,ll le,ll ri,ll ti)
{
	if(le<=l&&ri>=r)
	{
		t[wz].push_back(ti);
		return ;
	}
	ll mid=(l+r)/2;
	if(le<=mid) add(wz*2,l,mid,le,ri,ti);
	if(ri>mid) add(wz*2+1,mid+1,r,le,ri,ti);
}
void merge(ll x,ll y)
{
	ll fx=find(x),fy=find(y);
	if(h[fx]>h[fy]) swap(fx,fy);
	st[++top]={fx,fy,h[fx]==h[fy]};
	fa[fx]=fy;
	if(h[fx]==h[fy]) h[fy]++;
}
void BT(ll wz,ll l,ll r)
{
	bool flag=true;
	ll lasttop=top;
	for(ll i=0;i<t[wz].size();i++)
	{
		ll a=find(e[t[wz][i]].x);
		ll b=find(e[t[wz][i]].y);
		if(a==b)
		{
			for(ll k=l;k<=r;k++)
			puts("No");
			flag=false;
			break;
		}
		merge(e[t[wz][i]].x,e[t[wz][i]].y+n);
		merge(e[t[wz][i]].y,e[t[wz][i]].x+n);
	}
	if(flag)
	{
		if(l==r) puts("Yes");
		else
		{
			ll mid=(l+r)/2;
			BT(wz*2,l,mid);
			BT(wz*2+1,mid+1,r);
		}
	}
	while(top>lasttop)
	{
		h[fa[st[top].y]]-=st[top].add;
		fa[st[top].x]=st[top].x;
		top--;
	}
	return ;
}
int main()
{
	scanf("%lld %lld %lld",&n,&m,&k);
	for(ll i=1;i<=m;i++)
	{
		ll l,r;
		scanf("%lld %lld",&e[i].x,&e[i].y);
		scanf("%lld %lld",&l,&r);
		l++;
		add(1,1,k,l,r,i);
	}
	for(ll i=1;i<=2*n;i++) fa[i]=i,h[i]=1;
	BT(1,1,k);
	return 0;
}