ICode9

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

【数据结构】线段树分治

2022-07-12 21:32:18  阅读:205  来源: 互联网

标签:int 线段 分治 define 操作 数据结构 节点 rk


目录

线段树分治

本题做法

实现

线段树分治

事实上线段树分治的做法很简单,就是在时间轴上开线段树,以方便处理在一段时间内其效果的操作。

比如说,现在整棵线段树维护的时间范围是 \([1, 3]\),开出的线段树自然是:

现在有一个操作在时间 \([1, 2]\) 上作用,那么对应于线段树的节点就是:

又有一个操作在时间 \([2, 3]\) 上作用,对应于线段树的节点就是:

根据线段树的性质,每个操作至多被划分成 \(\rm{log}\) 个线段树节点上的操作,我们就将划分后的操作依次存入发生影响的线段树节点即可。

最后,我们先序遍历一遍这棵树,对于当前节点,使其存储的操作生效,然后在递归地访问左右子树后撤销即可。(这一步使用一个栈进行维护,操作生效时压栈,最后在弹栈的时候执行撤销)

本题做法

本题的操作是在一段时间内合并图上的两点(也就是连边),然后判断某时间是否为二分图,结合上面的线段树分治过程,直接在图上维护(例如考虑染色)当然很不方便。因此考虑使用扩展域并查集维护:而为了方便撤销操作,我们不可以进行路径压缩,只能采取按秩合并

实现

实现的思路是:

  • 先在时间轴 \([1, K]\) 建立线段树。

  • 首先将操作分配(assign)进线段树节点的 vector<pii> o 中。

  • 然后对整棵树进行询问,并相应地进行操作生效(这题是合并,也就是代码中的 merge)、撤销(resume)操作,

    • 如果当前区间会导出矛盾,那么我们可以直接在这个区间都输出 No
    • 否则,继续向左右子树递归直到叶节点,如果直到叶节点都没有矛盾,直接输出 Yes
    • 可以发现,因为是先序遍历,所以直接输出就能满足题目的输出要求。
// Problem: P5787 二分图 /【模板】线段树分治
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/P5787
// Memory Limit: 256 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
using namespace std;
 
#define debug(x) cerr << #x << ": " << (x) << endl
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define dwn(i,a,b) for(int i=(a);i>=(b);i--)
#define pb push_back
#define all(x) (x).begin(), (x).end()
 
#define x first
#define y second
using pii = pair<int, int>;
using ll = long long;
 
inline void read(int &x){
    int s=0; x=1;
    char ch=getchar();
    while(ch<'0' || ch>'9') {if(ch=='-')x=-1;ch=getchar();}
    while(ch>='0' && ch<='9') s=(s<<3)+(s<<1)+ch-'0',ch=getchar();
    x*=s;
}

const int N=2e5+50;

int n, m, K;

struct Msg{
	int x, y, z;
}stk[N];
int top;

struct Dsu{
	int fa[N], rk[N];
	
	void init(){
		rep(i,1,n<<1) fa[i]=i, rk[i]=1;
	}
	
	int find(int x){
		return x==fa[x]? x: find(fa[x]);
	}
	
	bool same(int x, int y){
		return find(x)==find(y);
	}
	
	void merge(int x, int y){
		x=find(x), y=find(y);
		if(x==y) return;
		if(rk[x]>rk[y]) swap(x, y);
		fa[x]=y;
		stk[++top]={x, y, rk[x]==rk[y]};
		if(rk[x]==rk[y]) rk[y]++;
	}
	
	void resume(Msg t){
		rk[t.y]-=t.z;
		fa[t.x]=t.x;
	}
}dsu;

struct Node{
	int l, r;
	vector<pii> o;
	
	#define ls u<<1
	#define rs u<<1|1
}tr[N<<2];

void build(int u, int l, int r){
	tr[u]={l, r};
	if(l==r) return;
	int mid=l+r>>1;
	build(ls, l, mid), build(rs, mid+1, r);
}

void assign(int u, int l, int r, int x, int y){
	if(l<=tr[u].l && tr[u].r<=r){
		tr[u].o.pb({x, y});
		return;
	}
	int mid=tr[u].l+tr[u].r>>1;
	if(l<=mid) assign(ls, l, r, x, y);
	if(mid<r) assign(rs, l, r, x, y);
}

void divi(int u){
	bool ng=false;
	int pre=top;
	for(auto &[x, y]: tr[u].o){
		dsu.merge(x, y+n);
		dsu.merge(y, x+n);
		if(dsu.same(x, x+n) || dsu.same(y, y+n)){
			ng=true;
			break;
		}
	}
	if(ng) rep(i,tr[u].l,tr[u].r) puts("No");
	else{
		if(tr[u].l==tr[u].r) puts("Yes");
		else{
			divi(ls);
			divi(rs);
		}
	}
	while(top!=pre){
		dsu.resume(stk[top--]);
	}
}

signed main(){
	cin>>n>>m>>K;
	build(1, 1, K);
	dsu.init();
	
	rep(i,1,m){
		int x, y, l, r; read(x), read(y), read(l), read(r);
		if(++l>r) continue;
		assign(1, l, r, x, y);
	}
	divi(1);
	
	return 0;
}

标签:int,线段,分治,define,操作,数据结构,节点,rk
来源: https://www.cnblogs.com/Tenshi/p/16471851.html

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

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

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

ICode9版权所有