ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

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

2021-12-16 15:35:20  阅读:163  来源: 互联网

标签:rt 连边 P5787 int 题解 线段 mid 区间 模板


分析

线段树分治呢,处理的就是这些基于时间上的物品加入删除问题,我们将所有事件离线处理,每一个询问代表一个时刻,对于每一个物品,处理它所存在的时段,概括一下就是用线段树来处理每一个物品能对哪些询问起作用。

所以线段树每一个节点就代表了一个时段,而在询问中我们通常是找某一个时间点,所以类似于标记永久化的思想,在从上到下的过程中,如果能覆盖一个区间,那区间内的点也能同样造成影响,所以我们先将每一个物品加到它对应的区间,用 vector 存储完全覆盖某一区间的物品有哪些。

然后我们就可以从大区间向小区间找,每到一个区间时,将完全覆盖该区间的边所带来的影响处理,然后将处理后的结果带给更小的区间。

现在知道了线段树分治了,考虑本题每加入一条边的影响。

是否形成二分图,就需要将点分为两类,使得每一条边的两侧都是不同类的点,我们再引入种类并查集。

就是将并查集扩大几倍,分为几部分,每部分表示每一个个体可能属于的种群,相当于每个个体在每一部分都有自己的点,每一部分内部的连边就相当于同类关系,不同部分间的连边就代表对应题目中所给出的种群间的某种特殊联系。

例如此题,我们将每个点拆为两个点,\(i\) 表示第一类,\(i+n\) 表示第二类,\(i\) 与 \(j+n\) 同一并查集内表示它们不同类,\(i\) 与 \(j\) 或 \(i+n\) 与 \(j+n\) 在同一并查集内表示它们同类,所以在连边时先判断两端点是否同类,如果同类则说明当前分治到的整个区间都不可行,因为下面只会连更多的边,不会抵消这个冲突,只可能带来更多的冲突,就可以直接回退。否则就将代表两者不同类的边连上。

分治中还存在的一个问题就是递归完左区间后,需要去除左区间对右区间的影响,所以此题的并查集不能路径压缩,因为在回到上一层时需将在该区间连上的边断掉,所以在连边时记录本次连边的儿子,以及父亲原并查集的深度(因为要保证按秩合并的正确性),在返回时倒序将状态还原即可。

代码

#include<bits/stdc++.h>
using namespace std;
#define ls rt<<1
#define rs rt<<1|1
inline void read(int &res){
	res=0;
	int f=1;
	char c=getchar();
	while(c<'0'||c>'9'){if(c=='-')f=-1;c=getchar();}
	while(c>='0'&&c<='9')res=(res<<1)+(res<<3)+c-48,c=getchar();
	res*=f;
}
int n,m,k;
int l[200005],r[200005],u[200005],v[200005];
int flag[200005][2];
int flagg[200005][2];
vector<int>V[400005];
void modify(int rt,int l,int r,int L,int R,int x){
	if(L>R)return;
	if(l>=L&&R>=r){
		V[rt].push_back(x);
		return;
	}
	int mid=(l+r)>>1;
	if(mid>=L)modify(ls,l,mid,L,R,x);
	if(mid<R)modify(rs,mid+1,r,L,R,x);
}
int fa[200005],siz[200005];
int find(int x){
	return fa[x]==x?x:find(fa[x]);
}
bool merch(int s){
	int x=u[s],y=v[s];
	if(find(x)==find(y))return 1;
	if(find(x+n)==find(y+n))return 1;
	int kx=find(x),ky=find(y+n);
	if(kx!=ky){
		if(siz[kx]<siz[ky]){//按秩合并基础操作 
			flag[s][0]=kx;//这次连边当儿子的是谁 
			flagg[s][0]=siz[ky];//他爹原来深度多少 
			fa[kx]=ky;
			siz[ky]=max(siz[ky],siz[kx]+1);
		}
		else {
			flag[s][0]=ky;
			flagg[s][0]=siz[kx];
			fa[ky]=kx;
			siz[kx]=max(siz[kx],siz[ky]+1);
		}
	}
	kx=find(x+n),ky=find(y);
	if(kx!=ky){
		if(siz[kx]<siz[ky]){
			flag[s][1]=kx;
			flagg[s][1]=siz[ky];
			fa[kx]=ky;
			siz[ky]=max(siz[ky],siz[kx]+1);
		}
		else {
			flag[s][1]=ky;
			flagg[s][1]=siz[kx];
			fa[ky]=kx;
			siz[kx]=max(siz[kx],siz[ky]+1);
		}
	}
	return 0;
}
void del(int x){
	if(flag[x][1]){
		int y=flag[x][1];
		int kx=find(y);
		siz[kx]=flagg[x][1];//将原状态复原 
		fa[y]=y;
	}
	if(flag[x][0]){
		int y=flag[x][0];
		int kx=find(y);
		siz[kx]=flagg[x][0];
		fa[y]=y;
	}
}
void sol(int rt,int l,int r){
	for(int i=0;i<V[rt].size();i++){
		int x=V[rt][i];
		if(merch(x)){
			for(int j=l;j<=r;j++)puts("No");//整个区间都不可行 
			for(int j=i-1;j>=0;j--){
				del(V[rt][j]);//倒序删除 
			}
			return;
		}
	}
	if(l==r){
		for(int i=V[rt].size()-1;i>=0;i--){ 
			del(V[rt][i]);
		}
		puts("Yes");
		return;
	}
	int mid=(l+r)>>1;
	sol(ls,l,mid);
	sol(rs,mid+1,r);
	for(int i=V[rt].size()-1;i>=0;i--){
		del(V[rt][i]);
	}
}
int main()
{
	read(n);read(m);read(k);
	for(int i=1;i<=m;i++){
		read(u[i]);read(v[i]);read(l[i]);read(r[i]);
		modify(1,1,k,l[i]+1,r[i],i);
	}
	for(int i=1;i<=2*n;i++)fa[i]=i,siz[i]=1;//预处理 
	sol(1,1,k);
}

标签:rt,连边,P5787,int,题解,线段,mid,区间,模板
来源: https://www.cnblogs.com/zxhmh/p/15698343.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有