首页 > 其他分享 >【数据结构】lxl 的 DS 修炼

【数据结构】lxl 的 DS 修炼

时间:2023-11-23 21:36:02浏览次数:31  
标签:lxl lc int pos valpre valsuf rc 数据结构 DS

线段树 & 平衡树

用线段树/平衡树维护的序列问题可以分为两类:

1.静态型:维护一个类似于 \(\sum_{l,r}....\) 的值,或者是多次询问区间或全局的一些特征值。

2.动态型:支持动态修改和动态询问区间信息的类型。

对于静态型,我们通常首先思考怎样求单个区间的答案值,同理,动态型通常先考虑不带修,也就是一个序列怎么做。

对于一个难以维护的题目,我们可以先写出要维护的信息,然后画出一个信息和另一个的依赖推导关系,最后得到闭包求出答案。

例如:

「Wdsr-2.7」文文的摄影布置

这题可以转化为:求区间内任意三元组 \((i,j,k)(i < j < k)\) 的 \(A_i - B_j + A_k\) 最大值。

考虑静态问题,观察能不能计算跨过分治中心的答案,架构序列分治的模型。我们发现有四种情况:

image

考虑 \(i,j,k\) 全在左右边,就是左右边单独的答案 \(\max\) 。考虑 \(i,j\) 在左边的情况:

首先由于 \(k\) 一定在右边,取 \(\max a_k\) 一定是最优的。所以左边要求 \(a_i - b_j\) 的最大值。

考虑左边的区间,同样地,我们发现, \(a_i - b_j\) 要么就是左边儿子的答案,要么就是右边儿子的答案,要么就是左边 \(\max a_i\) 减去右边 \(\min b_j\) ,这样我们将问题转化为了求 \(a,b\) 的最值,成功解决。

其他情况同样讨论即可。

按照上面的说法,这张图画下来就应该是这样的:

image

展开到最后一步,理清思路,再返回实现代码,很快就可以做完。

#include<bits/stdc++.h>
using namespace std;
const int N = 5e5 + 5,inf = 0x3f3f3f3f;
int n,m,A[N],B[N];
struct Node{
	int mxa,mnb,ansij,ansjk,ans;
};
Node operator +(Node x,Node y)
{
	Node z;
	z.mxa = max(x.mxa,y.mxa);
	z.mnb = min(x.mnb,y.mnb);
	z.ansij = max(max(x.ansij,y.ansij),x.mxa - y.mnb);
	z.ansjk = max(max(x.ansjk,y.ansjk),y.mxa - x.mnb);
	z.ans = max(max(x.ans,y.ans),max(x.ansij + y.mxa,x.mxa + y.ansjk));
	return z;
}
struct Segment_Tree{
	Node a[N << 2];
	inline void pushup(int pos) {a[pos] = a[pos << 1] + a[pos << 1 | 1];}
	inline void modify(int l,int r,int x,int k,int type,int pos)
	{
		if(l == r) {if(type == 1) a[pos].mxa = k; else a[pos].mnb = k; return;}
		int mid = (l + r) >> 1;
		if(x <= mid) modify(l,mid,x,k,type,pos << 1);
		else modify(mid + 1,r,x,k,type,pos << 1 | 1);
		pushup(pos);
	}
	inline Node query(int l,int r,int L,int R,int pos)
	{
		if(L <= l && r <= R) return a[pos];
		int mid = (l + r) >> 1; Node ret = {-inf,-inf,-inf,-inf,-inf};
		if(L <= mid) ret = query(l,mid,L,R,pos << 1);
		if(R > mid) {if(ret.mxa == -inf) ret = query(mid + 1,r,L,R,pos << 1 | 1); else ret = ret + query(mid + 1,r,L,R,pos << 1 | 1);}
		return ret;
	}
	inline void build(int l,int r,int pos)
	{
		if(l == r) {a[pos].mxa = A[l]; a[pos].mnb = B[l]; a[pos].ansij = a[pos].ansjk = a[pos].ans = -inf; return;}
		int mid = (l + r) >> 1;
		build(l,mid,pos << 1); build(mid + 1,r,pos << 1 | 1);
		pushup(pos); 
	}
}t;
int main()
{
	cin>>n>>m;
	for(int i = 1;i <= n;i++) cin>>A[i];
	for(int i = 1;i <= n;i++) cin>>B[i];
	t.build(1,n,1);
	Node ret = t.query(1,n,1,n,1);
	for(int i = 1,op,x,y;i <= m;i++)
	{
		cin>>op>>x>>y;
		if(op == 1 || op == 2) t.modify(1,n,x,y,op,1);
		else cout<<t.query(1,n,x,y,1).ans<< '\n';
	}
	return 0;
 } 
技巧

[HNOI2011] 括号修复 / [JSOI2011]括号序列

按照套路,首先考虑静态怎么做,发现可以转化:

尽量匹配所有的括号,删掉,剩下的一定形如 ))))))...((((((

所以我们只需要改成 ()()()()()()()()()()()()...

假设剩右括号有 \(p\) 个,左括号有 \(q\) 个,我们可以发现答案就是 \(\lceil \frac p2 \rceil + \lceil \frac q2 \rceil\) 。

解决了静态序列的问题,容易看出来这个东西是好合并的,讨论一下即可。

分开考虑维护几个操作:

1.Replace

区间推平,线段树上用一个标记即可,推平后整个区间的值可以 \(\Theta(1)\) 计算。

2.Swap

翻转串,这里意识到需要用平衡树,平衡树上 tag 照样可以维护第一个,所以套文艺平衡树即可,再用一个 tag。

3.Invert

取反,这个很不好做,先前再 Flower's Land 当中见过类似套路,观察到取反再取反就不变,状态数 \(\Theta(1)\) 个,我们同时维护 \(2\) 种状态的答案,取反时打标记再交换答案即可。

这样我们就用平衡树维护了操作,可以回答询问。但是笔者仍然调了两个小时,最后发现,问题出在 Swap 上,线段树区间操作的两大要求就是 懒标记的可并性当前区间被整个包含时 \(\Theta(1)\) 得出答案 。就是说翻转后的区间答案不一样,我们要求 \(\Theta(1)\) 算出,怎么办呢?

发现翻转这个东西也是只有 \(2\) 个状态的操作,所以我们再同时维护出翻转前后的答案即可。

这样,我们用 \(3\) 个标记,\(4\) 个状态大常数 \(\Theta(n \log n)\) 地维护出了信息。

注意标记顺序要将 Invert 放在第一位,并且取反后要将推平标记也取反。推平后要将取反标记归零。

#include<bits/stdc++.h>
using namespace std;
const int N = 3e5 + 5;
int n,q,a[N];
char s[N];
struct fhq{//正/反,换/不换 
	int val[2][N],valpre[2][2][N],valsuf[2][2][N],tot = 0,lc[N],rc[N],hp[N],siz[N],tagflip[N],tagrev[N],tagcov[N],root = 0,y,z,w,p;
	inline void pushup(int pos)
	{
		valpre[0][0][pos] = valpre[0][0][lc[pos]] + max(0,valpre[0][0][rc[pos]] + (val[0][pos] == 1 ? 1 : -1) - valsuf[0][0][lc[pos]]);
		valsuf[0][0][pos] = valsuf[0][0][rc[pos]] + max(0,valsuf[0][0][lc[pos]] + (val[0][pos] == 0 ? 1 : -1) - valpre[0][0][rc[pos]]);
		valpre[0][1][pos] = valpre[0][1][lc[pos]] + max(0,valpre[0][1][rc[pos]] + (val[1][pos] == 1 ? 1 : -1) - valsuf[0][1][lc[pos]]);
		valsuf[0][1][pos] = valsuf[0][1][rc[pos]] + max(0,valsuf[0][1][lc[pos]] + (val[1][pos] == 0 ? 1 : -1) - valpre[0][1][rc[pos]]);
		swap(lc[pos],rc[pos]);
		valpre[1][0][pos] = valpre[1][0][lc[pos]] + max(0,valpre[1][0][rc[pos]] + (val[0][pos] == 1 ? 1 : -1) - valsuf[1][0][lc[pos]]);
		valsuf[1][0][pos] = valsuf[1][0][rc[pos]] + max(0,valsuf[1][0][lc[pos]] + (val[0][pos] == 0 ? 1 : -1) - valpre[1][0][rc[pos]]);
		valpre[1][1][pos] = valpre[1][1][lc[pos]] + max(0,valpre[1][1][rc[pos]] + (val[1][pos] == 1 ? 1 : -1) - valsuf[1][1][lc[pos]]);
		valsuf[1][1][pos] = valsuf[1][1][rc[pos]] + max(0,valsuf[1][1][lc[pos]] + (val[1][pos] == 0 ? 1 : -1) - valpre[1][1][rc[pos]]);
		swap(lc[pos],rc[pos]);
		siz[pos] = siz[lc[pos]] + siz[rc[pos]] + 1;
	}
	inline void change_flip(int pos)
	{
		if(!pos) return;
		swap(val[0][pos],val[1][pos]);
		swap(valpre[0][0][pos],valpre[0][1][pos]); swap(valsuf[0][0][pos],valsuf[0][1][pos]);
		swap(valpre[1][0][pos],valpre[1][1][pos]); swap(valsuf[1][0][pos],valsuf[1][1][pos]);
		tagflip[pos] ^= 1;
		if(tagcov[pos] != -1) tagcov[pos] ^= 1;
	}
	inline void change_rev(int pos)
	{
		if(!pos) return;
		swap(lc[pos],rc[pos]); 
		swap(valpre[0][0][pos],valpre[1][0][pos]); swap(valpre[0][1][pos],valpre[1][1][pos]);
		swap(valsuf[0][0][pos],valsuf[1][0][pos]); swap(valsuf[0][1][pos],valsuf[1][1][pos]);
		tagrev[pos] ^= 1;
	}
	inline void change_cov(int pos,int x)
	{
		if(!pos) return;
		val[0][pos] = x; val[1][pos] = x ^ 1;
		if(x == 0) 
		{
			valpre[0][0][pos] = 0; valsuf[0][0][pos] = siz[pos];
			valpre[0][1][pos] = siz[pos]; valsuf[0][1][pos] = 0;
			valpre[1][0][pos] = 0; valsuf[1][0][pos] = siz[pos];
			valpre[1][1][pos] = siz[pos]; valsuf[1][1][pos] = 0;
		}
		else
		{
			valpre[0][0][pos] = siz[pos]; valsuf[0][0][pos] = 0;
			valpre[0][1][pos] = 0; valsuf[0][1][pos] = siz[pos];
			valpre[1][0][pos] = siz[pos]; valsuf[1][0][pos] = 0;
			valpre[1][1][pos] = 0; valsuf[1][1][pos] = siz[pos];
		}
		tagcov[pos] = x; tagflip[pos] = 0;
	}
	inline void pushdown(int pos)
	{
		if(tagflip[pos])
		{
			change_flip(lc[pos]); change_flip(rc[pos]);
			tagflip[pos] = 0;
		}
		if(tagrev[pos])
		{
			change_rev(lc[pos]); change_rev(rc[pos]);
			tagrev[pos] = 0;
		}
		if(tagcov[pos] != -1)
		{
			change_cov(lc[pos],tagcov[pos]); change_cov(rc[pos],tagcov[pos]);
			tagcov[pos] = -1;
		}
	}
	inline void split(int &x,int &y,int k,int pos)
	{
		if(!pos) {x = y = 0; return;}
		pushdown(pos);
		if(siz[lc[pos]] + 1 <= k) x = pos,split(rc[x],y,k - siz[lc[pos]] - 1,rc[pos]);
		else y = pos,split(x,lc[y],k,lc[pos]);
		pushup(pos);
	}
	inline int merge(int x,int y)
	{
		pushdown(x); pushdown(y);
		if(!x || !y) return x + y;
		if(hp[x] < hp[y])
		{
			rc[x] = merge(rc[x],y);
			pushup(x);
			return x;
		}
		else
		{
			lc[y] = merge(x,lc[y]);
			pushup(y);
			return y;
		}
	}
	inline int new_node(int x)
	{
		++tot;
		val[0][tot] = x; val[1][tot] = x ^ 1;
		valpre[0][0][tot] = x; valsuf[0][0][tot] = x ^ 1;
		valpre[0][1][tot] = x ^ 1; valsuf[0][1][tot] = x;
		valpre[1][0][tot] = x; valsuf[1][0][tot] = x ^ 1;
		valpre[1][1][tot] = x ^ 1; valsuf[1][1][tot] = x;
		lc[tot] = rc[tot] = 0;
		siz[tot] = 1;
		hp[tot] = 1ll * rand() * rand();
		return tot;
	}
	inline void ist(int x)
	{
		root = merge(root,new_node(x));
	}
	inline void cover(int l,int r,int x)
	{
		split(y,z,l - 1,root);
		split(w,p,r - l + 1,z);
		change_cov(w,x);
		root = merge(y,merge(w,p));
	}
	inline void reverse(int l,int r)
	{
		split(y,z,l - 1,root);
		split(w,p,r - l + 1,z);
		change_rev(w);
		root = merge(y,merge(w,p));
	}
	inline void flip(int l,int r)
	{
		split(y,z,l - 1,root);
		split(w,p,r - l + 1,z);
		change_flip(w);
		root = merge(y,merge(w,p));
	}
	inline int query(int l,int r)
	{
		split(y,z,l - 1,root);
		split(w,p,r - l + 1,z);
		int nowp = valpre[0][0][w],nows = valsuf[0][0][w];
		root = merge(y,merge(w,p));
		return (nowp + 1) / 2 + (nows + 1) / 2;
	}
}t;
int main()
{
	fill(t.tagcov,t.tagcov + N,-1);
	srand(time(NULL));
	cin>>n>>q;
	scanf("%s",s + 1);
	for(int i = 1;i <= n;i++) a[i] = (s[i] == '(') ? 0 : 1;
	for(int i = 1;i <= n;i++) t.ist(a[i]);
	string op; char d; int x,y;
	for(int i = 1;i <= q;i++)
	{
		cin>>op;
		if(op == "Replace")
		{
			cin>>x>>y>>d;
			t.cover(x,y,(d == ')') ? 1 : 0); 
		}
		else if(op == "Swap")
		{
			cin>>x>>y;
			t.reverse(x,y);
		}
		else if(op == "Invert")
		{
			cin>>x>>y;
			t.flip(x,y);
		}
		else if(op == "Query")
		{
			cin>>x>>y;
			cout<<t.query(x,y)<< '\n'; 
		}
	}
	return 0;
}

标签:lxl,lc,int,pos,valpre,valsuf,rc,数据结构,DS
From: https://www.cnblogs.com/TheLastCandy/p/17852543.html

相关文章

  • 解决黑群晖DSM7.2不能关机的问题
    近期因为换了平台,CPU从4C8T升级到了8C16T,所以将系统从918+换到了3617XS,从DSM7.1升到了7.2,但更换了平台之后,我发现一个重大问题,DSM7.2有一定概率会出现无法彻底关机/关机变成重启的情况,我连续关了五次机都变成了重启,关机根本不生效,只能使用poweroff命令强制断电,这对硬盘非常不......
  • # yyds干货盘点 # 大佬们,如何把某一列中包含某个值的所在行给删除
    大家好,我是皮皮。一、前言前几天在Python白银交流群【上海新年人】问了一个Pandas数据处理的问题,一起来看看吧。大佬们,如何把某一列中包含某个值的所在行给删除?比方说把包含电力这两个字的行给删除。这里【FANG.J】指出:数据不多的话,可以在excel里直接ctrlf,查找“电力”查找全部,然......
  • com.aspose.words word 转pdf问题
    在讲word转pdf的时候推荐使用以下代码publicstaticvoidmain(String[]args)throwsException{//加载要转换的Word文档。Documentdoc=newDocument("C:\\Temp\\input.doc");//要保存输出的PDF文件的位置。StringoutputFilenam......
  • 【论文阅读笔记】【OCR-End2End】 ESTextSpotter: Towards Better Scene Text Spottin
    ESTextSpotterICCV2023读论文思考的问题论文试图解决什么问题?场景文本端到端识别任务中,检测和识别两个任务的协同作用十分关键,然而以往的方法通常用一些十分隐式的方式来体现这种协同作用(sharedbackbone,sharedencoder,sharedquery…),不能完全释放这种两个任务相互......
  • DS-slam
    sudoapt-getinstalllibgflags-dev-ysudoaptinstalllibgoogle-glog-dev-ysudoapt-getinstallprotobuf-compilerlibprotobuf-dev-ysudoaptinstalllibgflags-devlibgoogle-glog-devliblmdb-dev-ysudoaptinstalllibleveldb-dev-ysudoaptinstalllibsna......
  • vue中watch、computed、methods的执行顺序
    一、默认加载情况如果watch不加immediate:true属性(页面初加载的时候,不会执行watch,只有值变化后才执行),则只执行computed(在mounted后执行);如果watch添加immediate:true属性(在beforeCreate后created前执行),则先执行watch、再执行computed;二、触发某一事件后先执行method,再watch,再......
  • # yyds干货盘点 # Pandas实现这列股票代码中10-12之间的股票筛出来
    大家好,我是皮皮。一、前言前几天在Python白银交流群【YVONNE......
  • P8907 [USACO22DEC] Making Friends P 题解
    明明看着不难的题目,却意外的卡人。思路考虑两头奶牛可以成为朋友条件是什么。存在一条路径连接这两头奶牛。且除去端点外的路径上的所有点的编号小于两端点的较小值。充分必要性都比较显然。如何维护。我们可以从小到大加入点,维护这些路径。对于每个点维护一个\(\text{se......
  • Aspose.Words使用word模板中的书签/域插入信息并导出
    首先我大概叙述一下我对这个东西的理解毕竟我也只是记录一下,确保下次自己在看的时候可以看懂,所以写的比较详细且傻瓜首先这个word文档不是凭空生成的,是你事先就把文档创建好的,里边的内容,格式都是实现创建好的只留下一些需要插入数据的地方,当然这些需要插入数据的地方也不是空着的......
  • C语言数据结构_查找并删除单链表中最大值结点并返回值
    代码实现1#include<stdio.h>2#include<stdlib.h>34typedefstructNode//定义一个结构体5{6floatdata;7structNode*next;8}Node;910Node*Chuangzao_LinkedList()//创建一个链表11{12Node*head=NULL;//......