首页 > 其他分享 >P2572 序列操作 题解

P2572 序列操作 题解

时间:2023-08-22 16:59:50浏览次数:69  
标签:rt rsum BST 题解 sum ans 序列 lsum P2572

link

对平衡树的懒标记的应用题,其实和线段树也差不多。

如果不考虑取反操作,那维护操作 \(5\) 就需要知道当前区间答案,当前区间前缀和后缀,因为在 push_up 时我们当前区间的答案肯定等于左区间的答案,右区间的答案以及左区间的后缀加上右区间的前缀这三者间的最大值。

但与线段树不同的一点是,平衡树不是像线段树那样一个节点表示一个区间,它的一个节点就是代表数列中的一个数,所以在 push_up 时其实是 \(3\) 个部分在合并:当前节点,左子树,右子树。

所以在更新答案时我们还要判断当前点是否是 \(1\),如果不是,就不能用左区间后缀加右区间前缀,因为他们中间存在一个 \(0\)。

但加入了操作 \(3\) 又该如何处理呢?

其实很简单,因为是 \(0\) 变 \(1\),\(1\) 变 \(0\),所以我们在维护 \(1\) 的答案的同时维护 \(0\) 的答案,取反的时候直接将 \(0\) 和 \(1\) 的信息 swap 就可以了。

注意:由于是维护数列,所以 FHQ 在 split 的时候是按照 \(size\) 分裂而不是按照 \(key\)。

细节有点多。

code
#include <cstdio>
#include <cstdlib>
#include <utility>
#include <algorithm>
#include <ctime>

using namespace std;

const int N = 1e5 + 5;

struct Treap {
	int tot, root;
	struct node {
		int l, r, key, rank, siz, sum[2], lsum[2], rsum[2], ans[2], tag1; //l, r为左右儿子下标,key为具体是0还是1,siz为子树大小,sum[0/1]为0/1个数,lsum[0/1]为前缀0/1个数,ans[0/1]为最长连续0/1个数,tag1为操作1和2的懒标记 
		bool tag2; //操作3的懒标记
	} a[N];
	inline int New(int val) {
		a[++tot].rank = rand(), a[tot].key = val;
		a[tot].siz = 1, a[tot].tag1 = -1, a[tot].tag2 = false;
		if (val) { 
			a[tot].lsum[1] = a[tot].rsum[1] = a[tot].sum[1] = a[tot].ans[1] = 1;
			a[tot].lsum[0] = a[tot].rsum[0] = a[tot].sum[0] = a[tot].ans[0] = 0;
		} else {
			a[tot].lsum[0] = a[tot].rsum[0] = a[tot].sum[0] = a[tot].ans[0] = 1;
			a[tot].lsum[1] = a[tot].rsum[1] = a[tot].sum[1] = a[tot].ans[1] = 0;
		}
		return tot;
	}
	inline void change(int rt, int opt) {
		if (!opt) { //操作1 
			a[rt].key = 0; 
			a[rt].sum[0] = a[rt].lsum[0] = a[rt].rsum[0] = a[rt].ans[0] = a[rt].siz;
			a[rt].sum[1] = a[rt].lsum[1] = a[rt].rsum[1] = a[rt].ans[1] = 0;
			a[rt].tag1 = 0, a[rt].tag2 = false; //当区间覆盖操作进行后,取反操作被覆盖。 
		} else if (opt == 1) { //操作2
			a[rt].key = 1; 
			a[rt].sum[0] = a[rt].lsum[0] = a[rt].rsum[0] = a[rt].ans[0] = 0;
			a[rt].sum[1] = a[rt].lsum[1] = a[rt].rsum[1] = a[rt].ans[1] = a[rt].siz;
			a[rt].tag1 = 1, a[rt].tag2 = false;
		} else { //操作3
			a[rt].key ^= 1;
			swap(a[rt].sum[0], a[rt].sum[1]), swap(a[rt].lsum[0], a[rt].lsum[1]), swap(a[rt].rsum[0], a[rt].rsum[1]), swap(a[rt].ans[0], a[rt].ans[1]);
			a[rt].tag2 ^= 1; //取反操作优先级更低。 
		}
	}
	inline void push_up(int rt) {
		a[rt].siz = a[a[rt].l].siz + a[a[rt].r].siz + 1;
		if (a[rt].key) { //分当前点是0/1两种情况讨论 
			a[rt].sum[1] = a[a[rt].l].sum[1] + a[a[rt].r].sum[1] + 1;
			a[rt].ans[1] = max(a[a[rt].l].rsum[1] + a[a[rt].r].lsum[1] + 1, max(a[a[rt].l].ans[1], a[a[rt].r].ans[1]));
			a[rt].lsum[1] = a[a[rt].l].lsum[1];
			if (a[a[rt].l].lsum[1] == a[a[rt].l].siz)
				a[rt].lsum[1] += 1 + a[a[rt].r].lsum[1];
			a[rt].rsum[1] = a[a[rt].r].rsum[1];
			if (a[a[rt].r].rsum[1] == a[a[rt].r].siz)
				a[rt].rsum[1] += 1 + a[a[rt].l].rsum[1];
			
			a[rt].sum[0] = a[a[rt].l].sum[0] + a[a[rt].r].sum[0];
			a[rt].ans[0] = max(a[a[rt].l].ans[0], a[a[rt].r].ans[0]);
			a[rt].lsum[0] = a[a[rt].l].lsum[0];
			a[rt].rsum[0] = a[a[rt].r].rsum[0];
		} else {
			a[rt].sum[0] = a[a[rt].l].sum[0] + a[a[rt].r].sum[0] + 1;
			a[rt].ans[0] = max(a[a[rt].l].rsum[0] + a[a[rt].r].lsum[0] + 1, max(a[a[rt].l].ans[0], a[a[rt].r].ans[0]));
			a[rt].lsum[0] = a[a[rt].l].lsum[0];
			if (a[a[rt].l].lsum[0] == a[a[rt].l].siz)
				a[rt].lsum[0] += 1 + a[a[rt].r].lsum[0];
			a[rt].rsum[0] = a[a[rt].r].rsum[0];
			if (a[a[rt].r].rsum[0] == a[a[rt].r].siz)
				a[rt].rsum[0] += 1 + a[a[rt].l].rsum[0];
			
			a[rt].sum[1] = a[a[rt].l].sum[1] + a[a[rt].r].sum[1];
			a[rt].ans[1] = max(a[a[rt].l].ans[1], a[a[rt].r].ans[1]);
			a[rt].lsum[1] = a[a[rt].l].lsum[1];
			a[rt].rsum[1] = a[a[rt].r].rsum[1];
		}
	}
	inline void push_down(int rt) {
		if (a[rt].tag1 != -1) {
			if (a[rt].l)
				change(a[rt].l, a[rt].tag1);
			if (a[rt].r)
				change(a[rt].r, a[rt].tag1);
			a[rt].tag1 = -1;
		}
		if (a[rt].tag2) {
			if (a[rt].l)
				change(a[rt].l, 2);
			if (a[rt].r)
				change(a[rt].r, 2);
			a[rt].tag2 = false;
		}
	}
	inline int merge(int rt1, int rt2) {
		if (!rt1)
			return rt2;
		if (!rt2)
			return rt1;
		if (a[rt1].rank < a[rt2].rank) {
			push_down(rt1);
			a[rt1].r = merge(a[rt1].r, rt2);
			push_up(rt1);
			return rt1;
		} else {
			push_down(rt2);
			a[rt2].l = merge(rt1, a[rt2].l);
			push_up(rt2);
			return rt2;
		}
	}
	inline pair<int, int> split(int rt, int val) {
		if (!rt)
			return make_pair(0, 0);
		push_down(rt);
		if (a[a[rt].l].siz + 1 <= val) {
			pair<int, int> ans = split(a[rt].r, val - a[a[rt].l].siz - 1);
			a[rt].r = ans.first;
			push_up(rt);
			return make_pair(rt, ans.second);
		} else {
			pair<int, int> ans = split(a[rt].l, val);
			a[rt].l = ans.second;
			push_up(rt);
			return make_pair(ans.first, rt);
		}
	}
} BST;
int n, m, opt, l, r;

int main() {
	srand(time(0));
	scanf("%d %d", &n, &m);
	for (int i = 1, x; i <= n; i++)
		scanf("%d", &x), BST.root = BST.merge(BST.root, BST.New(x));
	while (m--) {
		scanf("%d %d %d", &opt, &l, &r);
		l++, r++;
		if (!opt) {	//由于对l~r操作所以我们把它们裂出来,再改该根节点并更新懒标记 
			pair<int, int> ans1 = BST.split(BST.root, r);
			pair<int, int> ans2 = BST.split(ans1.first, l - 1);
			BST.change(ans2.second, 0); 
			BST.root = BST.merge(BST.merge(ans2.first, ans2.second), ans1.second);
		} else if (opt == 1) {
			pair<int, int> ans1 = BST.split(BST.root, r);
			pair<int, int> ans2 = BST.split(ans1.first, l - 1);
			BST.change(ans2.second, 1);
			BST.root = BST.merge(BST.merge(ans2.first, ans2.second), ans1.second);
		} else if (opt == 2) {
			pair<int, int> ans1 = BST.split(BST.root, r);
			pair<int, int> ans2 = BST.split(ans1.first, l - 1);
			BST.change(ans2.second, 2);
			BST.root = BST.merge(BST.merge(ans2.first, ans2.second), ans1.second);
		} else if (opt == 3) {
			pair<int, int> ans1 = BST.split(BST.root, r);
			pair<int, int> ans2 = BST.split(ans1.first, l - 1);
			printf("%d\n", BST.a[ans2.second].sum[1]);
			BST.root = BST.merge(BST.merge(ans2.first, ans2.second), ans1.second);
		} else {
			pair<int, int> ans1 = BST.split(BST.root, r);
			pair<int, int> ans2 = BST.split(ans1.first, l - 1);
			printf("%d\n", BST.a[ans2.second].ans[1]);
			BST.root = BST.merge(BST.merge(ans2.first, ans2.second), ans1.second);
		}
	}
	return 0;
}

标签:rt,rsum,BST,题解,sum,ans,序列,lsum,P2572
From: https://www.cnblogs.com/-I-AK-IOI-/p/17648874.html

相关文章

  • AGC032 A-D题解
    A最后一次插入的数的值与位置一定相同考虑倒着做每次从左往右扫一遍当遇到a[i]==i时将此数删除并跳出B当n为5时构造出的图如下(图形编辑器(csacademy.com))那么我们猜想当n为奇数时将n与其他点连边i与除了n-i的其他点连边证明:n的邻接点的编号之和为(n......
  • iOS开发之--获取验证码倒计时及闪烁问题解决方案
    大家在做验证码的时候一般都会用到倒计时,基本上大家实现的方式都差不多,先贴出一些代码来..-(void)startTime{__blockinttimeout=59;//倒计时时间dispatch_queue_tqueue=dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,0);dispatch_source......
  • 国标GB28181视频平台EasyGBS国标平台激活码授权提示“授权失败”的问题解决方案
    EasyGBS平台是基于国标GB28181协议的视频云服务平台,支持多路设备接入,并对多平台、多终端分发出RTSP、RTMP、FLV、HLS、WebRTC等多种格式的视频流。平台可为大数据等综合性监管平台提供极强的视频能力,既能作为能力平台为业务层提供接口调用,也可作为业务平台使用。有用户反馈,现场Easy......
  • [CSP-J 2021] 网络连接 题解
    传送门早期题解,转自博客QwQ本蒟蒻为数不多过了的黄题,祝贺!!!题面[CSP-J2021]网络连接题目描述TCP/IP协议是网络通信领域的一项重要协议。今天你的任务,就是尝试利用这个协议,还原一个简化后的网络连接场景。在本问题中,计算机分为两大类:服务机(Server)和客户机(Client)。服务机......
  • 「JLOI2014」松鼠的新家 题解
    「JLOI2014」松鼠的新家前言这道题倒也不是很难,只是有一些小坑需要避一下,可以看作半个LCA树上差分裸题。解析考虑维护一个树,点\(u\)表示每个房间需要的糖果数\(s_u\),而维尼在参观房间时从\(a\)到\(b\)就需要在\((a,\tob)\)的路径上的每个房间都摆上一个糖果,这时直......
  • [ABC098D] Xor Sum 2 题解
    题解传送门题目大意给出一个序列\(A\),求\(A_l\oplusA_{l+1}\oplus\dots\oplusA_r=A_l+A_{l+1}+\dots+A_r\)(\(\oplus\)即为\(xor\)异或)解析众所周知,异或是位运算中的一种不进位加法,即为如果两个\(bit\)相等返回\(0\),反之返回\(1\)。为什么说是不......
  • 「NOIP2008 普及组」ISBN 号码 题解
    前言转自博客,早期黑历史作品。这是本蒟蒻の第一篇题解qwq,发在博客上,还请多多关照.这道题是一道橙题,难度没有太大的问题,对于大犇们来说自然是一遍过的,本蒟就只能调调再交了.题面传送门题目描述每一本正式出版的图书都有一个ISBN号码与之对应,ISBN码包括99位数字、1......
  • 「NOIP2010」机器翻译 题解
    前言附加任务这道题也是一个简单模拟题。传送门解析这道题就是一个简单的模拟题,简单来说就是如果内存里面没有这个单词(其实是一个数)的话就从外存入队,如果内存容量不够,出队即可。对了,每次查询时还要计数噢!代码话不多说上代码#include<bits/stdc++.h>usingnamespacestd......
  • 「CSP-J2019」交通换乘 题解
    转自博客。传送门一道橙题,但是会T。题面[CSP-J2019]公交换乘题目描述著名旅游城市B市为了鼓励大家采用公共交通方式出行,推出了一种地铁换乘公交车的优惠方案:在搭乘一次地铁后可以获得一张优惠票,有效期为45分钟,在有效期内可以消耗这张优惠票,免费搭乘一次票价不超过地......
  • P3825 [NOI2017] 游戏 题解
    P3825[NOI2017]游戏题解首先解决没有x的情况,这种情况下每个事件有两种选择,例如a可以选择b,c,所以这就是一个2-SAT问题,但是这题比较特殊,除了题目中给的命题,还需要建立原命题的逆否命题所对应的边,最后跑一遍\(\text{Tarjan}\)就出解了。考虑有\(d\)个\(x\)的情况......