首页 > 其他分享 >P4666 [BalticOI 2011 Day1] Growing Trees题解(平衡树思想)

P4666 [BalticOI 2011 Day1] Growing Trees题解(平衡树思想)

时间:2024-02-27 09:22:07浏览次数:20  
标签:val la int 题解 ry Trees Day1 split rz

自己第一道不看题解写出来的紫题,庆祝一下(没初始化种子导致调了30min

这是一个 fhq-treap 的题解

思路来源:

首先看题目,因为是序列上的问题,不难想到是一道数据结构题。

首先看到操作 C:对于这种操作,我们可以用平衡树解决,具体方法是,将树 split 成 \(<min,min \le x \le max,>max\) 这三个部分,这个操作可以用按权值分裂解决,答案就是 \(ry\) 的子树大小(\(rx,ry,rz\)表示分裂出来的三棵树)。

其次考虑操作 F:我们可以把操作分成三部分

  1. 分裂出一颗满足树上权值大于 $ \ge h$ 的子树(直接按 \(val\) split)

  2. 找出权值前 \(c\) 大的点(直接按 \(siz\) split)

  3. 将树上每一个点加一

这是就会有聪明的小朋友要问了,操作 F 不是 \(O(n)\) 的吗?但是这个区间加让我们想到 lazytag,所以我们可以用 lazytag 将复杂度优化到 \(O(\log{n})\)

代码实现

思路可行,代码貌似也不难

看到操作 C 不难得出

	split(root,x-1,rx,ry);
	split(ry,y,ry,rz);
	cout<<size[ry]<<endl;
	root=merge(merge(rx,ry),rz);

看到操作 F 很快啊,我就把思路模拟出来了,可是连样例都没过。此时我们想到两颗 fhq-treap 能 merge 的前提是以 \(l\) 为根的子树的权值小于等于以 \(r\) 为根的子树。区间加的操作会破坏这个性质。这个也好解决,我们按照最暴力的方法:

这样就可以合并了:

	 split(root,y-1,rx,ry);
	 if(!ry) continue;
	 split1(ry,x,ry,rz);
	 la[ry]++;
	 p[ry].val++;
	 gg=p[getrnk(ry,size[ry])].val;
	 split(ry,gg-1,ry,ra);
	 split(rz,gg,rz,rb);
	 root=merge(merge(merge(merge(rx,ry),rz),ra),rb);

最后附上完整版(有问题多多留言哦)

#include<bits/stdc++.h>
using namespace std;
struct node{
	int val,rd;
}p[100005];
int n,m,x,y,cnt=0,rx,ry,rz,ra,rb,root,gg;
int size[100005],son[100005][2],la[100005];
char op;
inline int read(){
    int x=0; bool flag=1; char ch=getchar();
    while(ch<'0'||ch>'9') {if(ch=='-') flag=0; ch=getchar();}
    while(ch>='0'&&ch<='9') {x=(x<<1)+(x<<3)+ch-'0'; ch=getchar();}
    if(flag) return x;
    return ~(x-1);
}inline void write(int x){
    if(x<0) {x=~(x-1); putchar('-');}
    if(x>9) write(x/10);
    putchar(x%10+'0');
}
void push_up(int x){
	size[x]=size[son[x][0]]+size[son[x][1]]+1;
}
void push_down(int x){
	if(la[x]!=0){
		p[son[x][0]].val+=la[x];
		p[son[x][1]].val+=la[x];
		la[son[x][0]]+=la[x];
		la[son[x][1]]+=la[x];
		la[x]=0;
	}
}
int add(int x){
	cnt++;
	p[cnt].rd=rand();
	p[cnt].val=x;
	size[cnt]=1;
	return cnt;
}
void split(int rt,int key,int &x,int &y){
	if(!rt){
		x=y=0;
		return;
	}
	push_down(rt);
	if(p[rt].val<=key){
		x=rt;
		split(son[rt][1],key,son[rt][1],y);
	}else{
		y=rt;
		split(son[rt][0],key,x,son[rt][0]);
	}
	push_up(rt);
}
void split1(int rt,int siz,int &x,int &y){
	if(!rt){
		x=y=0;
		return;
	}
	push_down(rt);
	if(size[son[rt][0]]+1<=siz){
		x=rt;
		split1(son[rt][1],siz-(size[son[rt][0]]+1),son[rt][1],y);
	}else{
		y=rt;
		split1(son[rt][0],siz,x,son[rt][0]);
	}
	push_up(rt);
}
int merge(int l,int r){
	if(!l||!r){
		return l+r;
	}
	push_down(l),push_down(r);
	if(p[l].rd<p[r].rd){
		son[l][1]=merge(son[l][1],r);
		push_up(l);
		return l;
	}else{
		son[r][0]=merge(l,son[r][0]);
		push_up(r);
		return r;
	}
}
int getrnk(int x,int k){
	while(true){
		push_down(x);
		if((size[son[x][0]]+1)==k){
			return x;
		}else if((size[son[x][0]]+1)<k){
			k-=(size[son[x][0]]+1);
			x=son[x][1];
		}else x=son[x][0];
	}
}
int main(){
	srand(time(0));
	n=read(),m=read();
	for(int i=1;i<=n;i++){
		x=read();
		split(root,x,rx,ry);
		root=merge(merge(rx,add(x)),ry);
	}
	while(m--){
		op=getchar();
		x=read(),y=read();
		if(op=='F'){
			split(root,y-1,rx,ry);
			split1(ry,x,ry,rz);
			la[ry]++;
			p[ry].val++;
			gg=p[getrnk(ry,size[ry])].val;
			split(ry,gg-1,ry,ra);
			split(rz,gg,rz,rb);
			root=merge(merge(merge(merge(rx,ry),rz),ra),rb);
		}else{
			split(root,x-1,rx,ry);
			split(ry,y,ry,rz);
			write(size[ry]);
			putchar('\n');
			root=merge(merge(rx,ry),rz);
		}
	}
	
	return 0;
}

标签:val,la,int,题解,ry,Trees,Day1,split,rz
From: https://www.cnblogs.com/wuhupai/p/18036157

相关文章

  • Vue3学习(十九) - TreeSelect 树选择
    写在前面我知道自己现在的状态很不好,以为放个假能好好放松下心情,结果昨晚做梦还在工作,调试代码,和领导汇报工作。天呐,明明是在放假,可大脑还在考虑工作的事,我的天那,这是怎么了?Vue页面参数传递1、任务拆解页面跳转时带上当前电子书id参数ebookId新增/编辑文档时,读取电子书id......
  • AGC005D 题解
    传送门如果一个排列\(P\)满足对于所有的\(i\)都有\(|P_i-i|\neqk\),则称排列\(P\)为合法的。现给出\(n\)和\(k\),求有多少种合法的排列。由于答案很大,请输出答案对\(924844033\)取模的结果。\(2\leqn\leq2\times10^3\),\(1\leqk\leqn-1\)。一个新的trick:考虑......
  • winter week6 day1
    2024蓝桥杯模拟赛3(div1+div2)A[传智杯#3决赛]序列思路:暴力枚举查看代码#include<bits/stdc++.h>usingnamespacestd;#defineintlonglong//#defineint__int128#definedoublelongdoubletypedefpair<int,int>PII;typedefpair<string,int>PSI;typedefpa......
  • 2024.2.25模拟赛T3题解
    题目推出dp柿子之后,枚举\(i\)的时候用线段树维护\(1-i\)的\(mex\)段,对于每一段,分别使用线段树套李超树维护,对于每个\(mex\)再次使用线段树套李超树维护即可code#include<bits/stdc++.h>usingnamespacestd;#defineN600005#defineintlonglongintn,m;consti......
  • 2024.2.25模拟赛T1题解
    题目考虑DP式子之后,可以通过堆维护函数,求出对应值code#include<bits/stdc++.h>usingnamespacestd;#defineintlonglong#defineN200005intzu,n,d,tg,num;inta[N];priority_queue<int>q;signedmain(){ scanf("%lld",&zu); while(zu--){ scanf(&qu......
  • 2024.2.25模拟赛T2题解
    题目枚举根之后,考虑每次连边的贡献,通过贡献算出每个点的权值,每次找出权值最大的点,又要保证父亲在儿子之前,所以将父亲和儿子合并,权值也合并一下即可code#include<bits/stdc++.h>usingnamespacestd;#defineN2005intans,n,k;intsz[N],deg[N],du[N],fu[N],f[N],h[N];str......
  • 2024.2.26模拟赛T2题解
    题目对询问扫描线,建出\(PAM\)的失配树之后,每次查询相当于,把\(r\)对应节点到根路径染色之后,有多少个节点的值大于\(l\),可以树剖+ODT实现code#pragmaGCCoptimize("Ofast","inline","-ffast-math")#pragmaGCCtarget("avx,sse2,sse3,sse4,mmx")#include<bits/s......
  • 2024.2.26模拟赛T1题解
    题目先建出圆方树,题目转换为数长度为\(2*L-1\)的路径数,长链剖分code#include<bits/stdc++.h>usingnamespacestd;#defineN2000005#definelllonglongintn,m,top,tot,cnt,L,k;intdfn[N],low[N],zhan[N],h[N];structAB{ inta,b,n;}d[N*4];voidcun(intx,int......
  • P7154 Sleeping Cows 题解
    传送门题意:给定两个数组\(a_i,b_i\),若\(a_i\leb_j\),则他俩可配对。求极大匹配的方案数。(极大不是最大,最大一定是极大)先考虑最大匹配方案数怎么求。把\(a\)和\(b\)从小到大排序。则每个\(a_i\)能匹配的\(b\)都是一段后缀,且随着\(i\)增大,这个后缀越来越小。于是从......
  • P4708 画画 题解
    先叠层甲。此解法非本题正解如果想看正解可以去看上面\(Sdchr\)或\(Log_x\)大佬的。第一眼看到此题,\(N\)个点的无标号的每个连通块有欧拉回路的图的个数。这不就是\(N\)阶赛德尔矩阵的个数吗?什么?你不知道赛德尔矩阵是什么。没关系。这东西是个很小众的东西。百度上都......