首页 > 其他分享 >线段树分治

线段树分治

时间:2024-07-21 19:18:19浏览次数:12  
标签:ch int fy 线段 分治 height fa top

线段树分治

线段树分治可以解决这样的问题:

  1. 对于一些操作,每个操作只在 \(l\) ~ \(r\) 的的时间内有效。
  2. 对于一些操作,询问某个时间点所有操作的贡献。

经典 例题

算法思路

对于这道题,因为二分图的充要条件是 不存在奇环,所以可以使用 扩展域并查集 解决。
我们考虑离线优化:对于每个时间点建立一颗线段树,把操作挂到线段树上。我们使用样例模拟一下:
pk7VW40.png
我们对时间向右偏移一位:[0,3] 对应 [1,4]。但是 4 是消失的时间,所以存在的时间为 [1,3]。
pk7VhCV.png
上图为挂在线段树上的操作。

  1. 计算答案时,从根节点出发, 每到一个节点,将挂在这个节点上的所有边合并,然后递归处理左右儿子。如果发现某条边合并后会出现奇环,就输出 No
  2. 当到达叶子节点时,如果合并了所有挂在当前节点上的边,依旧满足二分图的性质,就输出 Yes
  3. 回溯时,由于并查集不支持删边,所以可以使用可撤销并查集(也就是用一个栈记录一下所有对并查集的操作)。因为它可撤销,所以不能路径压缩,为了保持算法时间上的正确性,要使用按秩合并。
#include<bits/stdc++.h>
using namespace std;
const int N = 10101010;
int read(){
	int x = 0,f = 0;char ch=getchar();
	while(!isdigit(ch)){if(ch=='-')f=1;ch=getchar();}
	while(isdigit(ch)){x=x*10+ch-'0';ch=getchar();}
	return f?-x:x;
}

int n,m,k,fa[N],height[N],top;
struct E{int x,y;}e[N];
struct Stack{int x,y,add;}st[N];
vector<int> t[N];

int findfa(int x)
{
	while(x != fa[x]) x = fa[x];
	return fa[x];
}
void debug()
{
	printf("\n****************\n下标");
	for(int i = 1;i <= n*2;i++) printf("%d ",i); 
	printf("\n父亲");
	for(int i = 1;i <= n*2;i++) printf("%d ",fa[i]);
	printf("\n祖先(代表元)");
	for(int i = 1;i <= n*2;i++) printf("%d ",findfa(i));
}
void merge(int x,int y)
{
	int fx = findfa(x),fy = findfa(y);
	if(height[fx] > height[fy]) swap(fx,fy); // 按秩合并
	st[++top] = (Stack){fx,fy,height[fx] == height[fy]}; // 用一个栈记录信息
	fa[fx] = fy;
	if(height[fx] == height[fy]) height[fy]++;
}
void update(int u,int l,int r,int L,int R,int x)
{
	if(l > R || r < L) return;
	if(L <= l && r <= R) {t[u].push_back(x);return;}
	int mid = l + r >> 1;
	update(u<<1,l,mid,L,R,x);
	update(u<<1|1,mid+1,r,L,R,x);
}
void solve(int u,int l,int r)
{
//	debug();
	int ans = 1; // 是不是二分图
	int lasttop = top;
	// 如果当前区间有时间线,若没有环就合并,有环就输出
	for(int i = 0;i < t[u].size();i++)
	{
		int a = findfa(e[t[u].at(i)].x);
		int b = findfa(e[t[u].at(i)].y);
		if(a == b)
		{
			for(int k = l;k <= r;k++)
			printf("No\n");
			ans = 0;
			break;
		}
		merge(e[t[u].at(i)].x,e[t[u].at(i)].y+n);
		merge(e[t[u].at(i)].y,e[t[u].at(i)].x+n);
	}
	if(ans) // 如果是二分图,就继续遍历儿子
	{
		if(l==r) printf("Yes\n");
		else 
		{
			int mid = l+r>>1;
			solve(u<<1,l,mid);
			solve(u<<1|1,mid+1,r);
		}
	}
	// 回溯,如果当前区间做过合并操作,就逐个撤销
	while(top > lasttop)
	{
		height[fa[st[top].x]] -= st[top].add;
		fa[st[top].x] = st[top].x;
		top--;
	}
	return;
}
int main()
{
	n = read();m = read();k = read();
	for(int i = 1;i <= m;i++)
	{
		e[i].x = read();e[i].y = read();
		int l = read()+1,r = read();
		update(1,1,k,l,r,i);
	}
	for(int i = 1;i <= 2*n;i++) fa[i] = i,height[i] = 1;
	solve(1,1,k);
	return 0;
}

标签:ch,int,fy,线段,分治,height,fa,top
From: https://www.cnblogs.com/legendcn/p/18314858

相关文章

  • 线段树优化建图
    $\quad$在做题时,我们会遇到这种问题:区间性的连边。$\quad$显然,直接连边很容易\(T\)掉,而且内存占用也是我们无法接受的,所以我们就可以采用一种更加方便(其实看起来更麻烦)的方法--线段树优化建图。$\quad$首先我们要有一棵入树与出树(这里用一下_ducati的图)$\quad$入树......
  • 线段树优化建图
    首先看这个问题:一张\(N\)个点的有向图,初始没有任何边,有\(M\)次操作:建\(1\)条边\(u\rightarrowv\),边权为\(w\)。建\(r-l+1\)条边\(u\rightarrow\foralli\in[l,r]\),边权为\(w\)。建\(r-l+1\)条边\(\foralli\in[l,r]\rightarrowu\),边权为\(w\)。求建完边......
  • 【学习笔记】线段树优化建图
    前言2023.5.31贺了线段树优化建图板子。当时那段时间还被\(bobo\)一顿乱\(D\),让我多写点\(DP\),数学,少些点重复的数据结构。2024.7.19没想到暑假集训CSP提高模拟2\(T3\)放了个线段树优化建图板子,加上之前线段树优化建图代码是贺的,今年寒假本想找时间步一下的结果没去......
  • 线段树
    把给定的区间转换成如图所示的一棵二叉树每次把区间一分为2,左边是左儿子,右边是右儿子对于每个节点的信息,都可以由两个儿子的信息得到如何单点查询/修改可以发现,两个儿子处理的区间没有交集,所以每次只要判断是在左儿子还是在右儿子,不断的递归对于区间查询,每一......
  • 线段树(原理、构造和区间查询,例题:Balanced Lineup)
    概念原理    线段树是分治法和二叉树的结合,二叉树上的节点都是根据分治得到的。节点所表示的,也就是线段,可以是区间和、最值或者是其他的,,每次分治,左右子树各一半,每个节点的值代表了以它为根的子树上所有节点的值。通过线段树,大区间的解可以从小区间的解合并而来。构......
  • 线段树优化建图
    为什么?什么时候用线段树优化建图例题如果此时暴力建边\(O(n^{2})\)肯定会TLE观察到题目中的“区间”此时考虑用线段树优化建图,在每个区间上连边(线段树上只有\(\log{n}\)个区间)来减少边的个数实现方法?摘抄自tzx_wk我们就拿\(2\)操作来举例吧。现在假设假如有一个点......
  • 关于线段树优化建图
    线段树优化建图引入对于这道板子题但是我不会大概意思就是:有\(n\)个点、\(q\)次操作。每一种操作为以下三种类型中的一种:连一条\(u→v\)的有向边,权值为w对于所有\(i∈[l,r]\)连一条\(u→i\)的有向边,权值为\(w\)对于所有\(i∈[l,r]\)连一条\(i→u\)的......
  • Legacy(线段树优化建图)
    CF786B-Legacy线段树优化建图板子题。。。。。。暴力建边\(\mathcalO(n^2)\))肯定会\(TLE\)但仔细分析可以发现,题面中有一个我们非常熟悉的字眼“区间”,这启示我们,可不可以以此作为解题的突破口呢?答案是肯定的。想到区间我们可以联想到各种我们很熟悉的\(trick\),如前缀和、......
  • 线段树优化建图
    线段树优化建图用途:处理区间连边做法:建两颗线段树,一颗处理区间的入边,另一颗处理出边(如果用一颗线段树的话,边权就都为0了)例题:Legacy板子题,直接看代码点此查看代码#include<bits/stdc++.h>#include<bits/extc++.h>//usingnamespace__gnu_pbds;//usingnamespa......
  • P3373 【模板】线段树 2(区间乘+加操作,先乘后加原则)
    题目来源:https://www.luogu.com.cn/problem/P3373//题意:对区间[l,r]可以乘法,加法操作,查询和操作。//思路:既有乘法又有加法,肯定是要有两个标记。纯加法和纯乘法操作是很简单的,但是既有乘法又有加法涉及到先乘后加和先加后乘的顺序。//////所以现在是如何将先加后成也可以......