数据结构注意端点、初始化、和数据范围
并查集
基础并查集:
using namespace std;
int bin[N]
int findd(int x){//查找根节点
int next=x;
while(bin[next]!=next)
next=bin[next];
return next;
}
void merge(int x,int y){//合并
int fx,fy;
fy=findd(y);
fx=findd(x);
if(fx!=fy)
bin[fx]=fy;
}
int main(){
for( int i=1;i<=n;i++)
bin[i]=i;//注意集合的初始化
}
并查集的合并优化:
using namespace std;
int bin[N]
int height[N]//注意需要初始化为0
int findd(int x){//查找根节点
int next=x;
while(bin[next]!=next)
next=bin[next];
return next;
}
void merge(int x,int y){//合并的优化
y=findd(y);
x=findd(x);
if(height[x]==height[y]){
height[x] = height[y] +1;//x的树高+1
bin[y]=x;//y向x合并
}
else{
if(height[x]<height[y]) bin[x]=y;
//如果y比较高,就将x合并在y上,同时y的树高不变
else bin[y]=x;
}
}
int main(){
for( int i=1;i<=n;i++)
bin[i]=i;//注意集合的初始化
}
并查集查找的优化:
int find(int x){
if( x!= s[x] ) s[x]=find(s[x]);
//在查找的路径上把经过节点的父节点改为根节点
return s[x];
}
//非递归方法见黑书
树状数组
树状数组的功能是单点修改和区间查询
int a[100100];
int c[100100];
int n;
//a为原数组,c为树状数组,下同
void creat(){
for( int i=1;i<=n;i++){
for( int j=0;j<lowbit(i);j++){
c[i]+=a[i-j];
}
}
}
int query( int x){
int sum=0;
while(x>0){
sum+=c[x];
x-=lowbit(x);
}
return sum;
}
void update( int i,int val){
while(i<=n){
c[i]+=val;
i+=lowbit(i);
}
}
线段树:
线段树的功能是区间修改,单点查询,区间查询
感觉位运算和普通乘法在运行效率上没有明现区别
没有lazy标记的线段树
***输入为a[N]
***
***
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define N 100100
struct node {
int l,r,val;
}tr[N*4];
int a[N];
int creat(int rt,int l,int r){
tr[rt].l=l,tr[rt].r=r;
if(l==r) {
tr[rt].val=a[l];
return a[l];
}
int mid=(l+r)/2,res=0;
res+=creat(rt*2,l,mid);
res+=creat(rt*2+1,mid+1,r);
tr[rt].val=res;
return res;
}
int qq( int rt,int L,int R){
int l=tr[rt].l,r=tr[rt].r;
if(l>=L&&r<=R) return tr[rt].val;
if(r<L||l>R) return 0;//有的模板会省略这句,个人感觉不妥
int mid=(l+r)/2;
int res=0;
if(L<=mid) res+=qq(rt*2,L,R);
if(R>mid) res+=qq(rt*2+1,L,R);
return res;
}
void up(int val,int pos,int rt){
int l=tr[rt].l,r=tr[rt].r;
if(l==r) tr[rt].val=val;
if(l>pos||r<pos) return ;
int mid=(l+r)/2;
if(pos<=mid) up(val,pos,rt*2);
else up(val,pos,rt*2+1);
tr[rt].val=tr[rt*2].val+tr[rt*2+1].val;
}
void mul( int val,int pos,int rt){//将某一个数乘上val倍
int l=tr[rt].l,r=tr[rt].r;
if(l==r) tr[rt].val*=val;
if(l>pos||r<pos) return ;
int mid=(l+r)/2;
if(pos<=mid) up(val,pos,rt*2);
else up(val,pos,rt*2+1);
tr[rt].val=tr[rt*2].val+tr[rt*2+1].val;
}
int main(){
int n,m;
cin>>n>>m;
for( int i=1;i<=n;i++){
cin>>a[i];
}
creat(1,1,n);
}
有lazy标记线段树
**lazy标记的含义是:**当前节点已经更新,但是子节点还差lazy没有更新
struct node{
int l,r,val;
int lz;
}tr[N<<2];//开四倍空间
void creat(int rt,int l,int r){
tr[rt].l=l;tr[rt].r=r;
tr[rt].lz=xxx;//初始化lz标记
if(l==r) {
tr[rt].val=xxx;//维护叶子结点
return;
}
int mid=(l+r)/2;
creat(rt*2,l,mid);
creat(rt*2+1,mid+1,r);
tr[rt].val=xxx;//维护根节点
return ;
}
void pushdown(int rt){
if(tr[rt].lz==xxx) return ;//如果lz标记不存在
int l=tr[rt].l,r=tr[rt].r;
int mid=(l+r)/2;
int ln=mid-l+1,rn=r-mid;
tr[rt*2].val=min(tr[rt].lz,tr[rt*2].val);//维护左子树
tr[rt*2+1].val=min(tr[rt].lz,tr[rt*2+1].val);//维护右子树
tr[rt*2].lz=min(tr[rt*2].lz,tr[rt].lz);//向左子树传递lz标记
tr[rt*2+1].lz=min(tr[rt*2+1].lz,tr[rt].lz);//向右子树传递lz标记
tr[rt].lz=inf;//清除lz标记
return ;
}
void up_add(int rt,int L,int R,int val){
int l=tr[rt].l,r=tr[rt].r;
if(L>r||R<l) return ;
if(l==r) {
tr[rt].val=xxx;//维护叶子结点
return ;
}
if(l>=L&&r<=R) {
int len=r-l+1;
tr[rt].val=min(tr[rt].val,val);//维护val
tr[rt].lz=min(tr[rt].lz,val);//更新lz标记
return ;
}
pushdown(rt);
int mid=(l+r)/2;
//
if(mid>=L) up_add(rt*2,L,R,val);
if(mid<R) up_add(rt*2+1,L,R,val);
tr[rt].val=min(tr[rt*2].val,tr[rt*2+1].val);
}
int qq(int rt,int L,int R){
int l=tr[rt].l,r=tr[rt].r;
if(l>R||r<L) return xxx;
if(l==r) return tr[rt].val;
pushdown(rt);
if(l>=L&&r<=R) return tr[rt].val;
int mid=(l+r)/2;
//维护返回值
int res=xxx;
if(L<=mid) res=min(qq(rt*2,L,R),res);
if(R>mid) res=min(qq(rt*2+1,L,R),res);
return res;
}
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define N 100100
ll mol;
int a[100100];
struct node{
int l,r,lz_add,lz_mul;
ll val;
}tr[N<<2];
void creat(int rt,int l,int r){
tr[rt].l=l;tr[rt].r=r;
tr[rt].lz_add=0;tr[rt].lz_mul=1;
if(l==r) {
tr[rt].val=a[l];
return;
}
int mid=(l+r)/2;
creat(rt*2,l,mid);
creat(rt*2+1,mid+1,r);
tr[rt].val=tr[rt*2].val+tr[rt*2+1].val;
return ;
}
void pushdown(int rt){
if(tr[rt].lz_add==0&&tr[rt].lz_mul==1) return ;
int l=tr[rt].l,r=tr[rt].r;
int mid=(l+r)/2;
int ln=mid-l+1,rn=r-mid;
if(tr[rt].lz_add!=0){
tr[rt*2].val+=tr[rt].lz_add*ln;
tr[rt*2+1].val+=tr[rt].lz_add*rn;
tr[rt*2].lz_add+=tr[rt].lz_add;
tr[rt*2+1].lz_add+=tr[rt].lz_add;
tr[rt].lz_add=0;
}
if(tr[rt].lz_mul!=1) {
tr[rt*2].val*=tr[rt].lz_mul%mol;
tr[rt*2+1].val*=tr[rt].lz_mul%mol;
tr[rt*2].lz_mul*=tr[rt].lz_mul;
tr[rt*2+1].lz_mul*=tr[rt].lz_mul;
tr[rt].lz_mul=1;
}
return ;
}
void up_add(int rt,int L,int R,int val){
int l=tr[rt].l,r=tr[rt].r;
if(L>r||R<l) return ;
if(l==r) {
tr[rt].val+=val;return ;
}
if(l>=L&&r<=R) {
int len=r-l+1;
tr[rt].val+=len*val;
tr[rt].lz_add+=val;
return ;
}
pushdown(rt);
int mid=(l+r)/2;
if(mid>=L) up_add(rt*2,L,R,val);
if(mid<R) up_add(rt*2+1,L,R,val);
tr[rt].val=tr[rt*2].val+tr[rt*2+1].val;
}
void up_mul( int rt,int L,int R,int val){
int l=tr[rt].l,r=tr[rt].r;
if(L>r||R<l) return ;
if(l==r) {
tr[rt].val*=val;
tr[rt].val%=mol;
return ;
}
if(l>=L&&r<=R) {
int len=r-l+1;
tr[rt].val*=val;
tr[rt].lz_mul*=val;
tr[rt].val%=mol;
tr[rt].lz_mul%=mol;
return ;
}
pushdown(rt);
int mid=(l+r)/2;
if(mid>=L) up_mul(rt*2,L,R,val);
if(mid<R) up_mul(rt*2+1,L,R,val);
tr[rt].val=tr[rt*2].val+tr[rt*2+1].val;
}
ll qq(int rt,int L,int R){
int l=tr[rt].l,r=tr[rt].r;
if(l>R||r<L)return 0;
if(l==r) return tr[rt].val;
pushdown(rt);
if(l>=L&&r<=R) return tr[rt].val;
int mid=(l+r)/2;
ll res=0;
if(L<=mid) res+=qq(rt*2,L,R);
if(R>mid) res+=qq(rt*2+1,L,R);
res%=mol;
return res;
}
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define N 100100
ll mol;
***输入数据是a
***需要区间加法修改1
***需要区间乘法修改2
***需要多个重叠区间区间最大值3
***需要区间最小值4
***数据范围修改N,注意ll
int a[N];
struct node{
int l,r;
1111//int lz_add;
2222//int lz_mul;
3333//int maxx;int lz_maxx;
4444//int minn;int lz_minn;
ll val;
}tr[N<<2];
void creat(int rt,int l,int r){
tr[rt].l=l;tr[rt].r=r;
1111//tr[rt].lz_add=0;
2222//tr[rt].lz_mul=1;
3333//tr[rt].lz_maxx=-inf;
4444//tr[rt].lz_minn=inf;
if(l==r) {
11112222//tr[rt].val=a[l];
3333//tr[rt].maxx=a[l];
4444//tr[rt].minn=a[l];
return;
}
int mid=(l+r)/2;
creat(rt*2,l,mid);
creat(rt*2+1,mid+1,r);
11112222//tr[rt].val=tr[rt*2].val+tr[rt*2+1].val;
3333//tr[rt].minn=max(tr[rt*2].maxx,tr[rt*2+1].maxx);
4444//tr[rt].minn=min(tr[rt*2].minn,tr[rt*2+1].minn);
return ;
}
void pushdown(int rt){
1111// if(tr[rt].lz_add==0) return ;
2222// if(tr[rt].lz_mul==1) return ;
3333// if(tr[rt].lz_maxx==-inf) return ;
4444// if(tr[rt].lz_minn==inf) return ;
int l=tr[rt].l,r=tr[rt].r;
int mid=(l+r)/2;
int ln=mid-l+1,rn=r-mid;
1111
// tr[rt*2].val+=tr[rt].lz_add*ln;
// tr[rt*2+1].val+=tr[rt].lz_add*rn;
// tr[rt*2].lz_add+=tr[rt].lz_add;
// tr[rt*2+1].lz_add+=tr[rt].lz_add;
// tr[rt].lz_add=0;
2222
// tr[rt*2].val*=tr[rt].lz_mul%mol;
// tr[rt*2+1].val*=tr[rt].lz_mul%mol;
// tr[rt*2].lz_mul*=tr[rt].lz_mul;
// tr[rt*2+1].lz_mul*=tr[rt].lz_mul;
// tr[rt].lz_mul=1;
3333
// tr[rt*2].maxx=max(tr[rt].lz_maxx,tr[rt*2].maxx);
// tr[rt*2+1].maxx=max(tr[rt].lz_maxx,tr[rt*2+1].maxx);
// tr[rt*2].lz_maxx=max(tr[rt*2].lz_maxx,tr[rt].lz_maxx);
// tr[rt*2+1].lz_maxx=max(tr[rt*2+1].lz_maxx,tr[rt].lz_maxx);
// tr[rt].lz_maxx=-inf;
4444
// tr[rt*2].minn=min(tr[rt].lz_minn,tr[rt*2].minn);
// tr[rt*2+1].minn=min(tr[rt].lz_minn,tr[rt*2+1].minn);
// tr[rt*2].lz_minn=min(tr[rt*2].lz_minn,tr[rt].lz_minn);
// tr[rt*2+1].lz_minn=min(tr[rt*2+1].lz_minn,tr[rt].lz_minn);
// tr[rt].lz_minn=inf;
return ;
}
void update(int rt,int L,int R,int val){
int l=tr[rt].l,r=tr[rt].r;
if(L>r||R<l) return ;
if(l==r) {
如果不是区间重叠而是更新的话需要在本if和下一个if修改
1111//tr[rt].val+=val;
2222//tr[rt].val*=val;
3333//tr[rt].maxx=max(val,tr[rt].maxx);
4444//tr[rt].minn=min(val,tr[rt].minn);
return ;
}
if(l>=L&&r<=R) {
int len=r-l+1;
1111
// tr[rt].val+=len*val;
// tr[rt].lz_add+=val;
2222
// tr[rt].val*=val;
// tr[rt].lz_mul*=val;
3333
//tr[rt].maxx=max(tr[rt].maxx,val);
//tr[rt].lz_maxx=max(tr[rt].lz_maxx,val);
4444
//tr[rt].minn=min(tr[rt].minn,val);
//tr[rt].lz_minn=min(tr[rt].lz_minn,val);
return ;
}
pushdown(rt);
int mid=(l+r)/2;
if(mid>=L) update(rt*2,L,R,val);
if(mid<R) update(rt*2+1,L,R,val);
1111//tr[rt].val=tr[rt*2].val+tr[rt*2+1].val;
2222//tr[rt].val=tr[rt*2].val+tr[rt*2+1].val;
3333//tr[rt].maxx=maxx(tr[rt*2].maxx,tr[rt*2+1].maxx);
4444//tr[rt].minn=minn(tr[rt*2].minn,tr[rt*2+1].minn);
}
ll qq(int rt,int L,int R){
int l=tr[rt].l,r=tr[rt].r;
if(l>R||r<L){
1111//return 0;
2222//return 0;
3333//return -inf;
4444//return inf;
}
if(l==r){
1111//return tr[rt].val;
2222//return tr[rt].val;
3333//return tr[rt].maxx;
4444//return tr[rt].minn;
}
pushdown(rt);
if(l>=L&&r<=R) {
1111//return tr[rt].val;
2222//return tr[rt].val;
3333//return tr[rt].maxx;
4444//return tr[rt].minn;
}
int mid=(l+r)/2;
1111//ll res=0;
2222//ll res=0;
3333//ll res=-inf;
4444//ll res=inf;
1111
// if(L<=mid) res+=qq(rt*2,L,R);
// if(R>mid) res+=qq(rt*2+1,L,R);
2222
// if(L<=mid) res+=qq(rt*2,L,R);
// if(R>mid) res+=qq(rt*2+1,L,R);
3333
// if(L<=mid) res=max(qq(rt*2,L,R),res);
// if(R>mid) res=max(qq(rt*2+1,L,R),res);
4444
// if(L<=mid) res=min(qq(rt*2,L,R),res);
// if(R>mid) res=min(qq(rt*2+1,L,R),res);
return res;
}
KMP算法
#include<iostream>
#include<stdio.h>
#include<string.h>
using namespace std;
int p[50100];
char a[50100];
char b[50100];
int la,lb;
void pre(char *b){
int j=0;p[1]=0;
for( int i=1;i<lb;i++){//注意这里是从1开始,
//因为量字符串要错开
while(b[i+1]!=b[j+1]&&j>0){
j=p[j];
}
if(b[i+1]==b[j+1]) j++;
p[i+1]=j;//上面判断的是i+1,所以这里是i+1
}
}
void solve(){
int j=0;
for( int i=0;i<lb;i++){
//注意这里要从0开始,因为在判断的是i+1
while(b[i+1]!=a[j+1]&&j){
j=p[j];
}
if(b[i+1]==a[j+1]){
j++;
}
if(){
}
}
}
int main(){
while(~scanf("%s%s",a+1,b+1)){
la=strlen(a+1);
lb=strlen(b+1);
memset(p,0,sizeof(p));
//一般而言p无需初始化,只要不越界就可以
maxx=0;
pre(a);
solve();
}
}
int Pre[100100];
string s,p;
void get_Pre(string p){
Pre[0]=0,Pre[1]=0;
for( int i=1;i<b.size();i++){
int j=Pre[i];
while(p[i]!=p[j]&&j) j=Pre[j];
Pre[i+1] = (p[i]==p[j]) ? j+1:0;
}
}
int kmp( string s,string p){
int last=-1;
get_Pre(p);
for( int i=0;i<s.size();i++){
while(j&&s[i]!=p[i]) j=Pre[j];
if(s[i]==p[j]) j++;
if(j==p.size()) {
//匹配成功
}
}
}
st表
st表可以求解区间最大值和最小值,不支持动态修改
#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
#define N 200100
int st[N][20];
int bin[20],Log[N];
int main(){
int n;
bin[0]=1;
for( int i=1;i<20;i++) bin[i]=bin[i-1]*2;
//计算2的i次方
Log[0]=-1;
for( int i=1;i<=N;i++)
Log[i]=Log[i/2]+1;
for( int i=1;i<=n;i++){
st[i][0]=a[i];
}
for( int i=1;i<=Log[n];i++){//枚举区间长度
for( int j=1;j<=n;j++)//枚举区间开始点
if(j+bin[i]-1<=n)
st[j][i]=min(st[j][i-1],st[j+bin[i-1]][i-1]);
}
//查询:
int t=Log[r-l+1];
printf("%d",min(st[l][t],st[r-bin[t]+1][t])) ;
}
字典树
typedef long long ll;
const int N=1001000;
struct node {
int cnt;
int ne[26];
}tr[N];
int cnt=1;
void insert(string s ){
int p=0;
for( int i=0;i<s.size();i++){
int a=s[i]-'a';
if(tr[p].ne[a]==0) tr[p].ne[a]=cnt++;
p=tr[p].ne[a];
}
tr[p].cnt++;
}
int find(string s){
int p=0;
int res=0;
for( int i=0;i<s.size();i++){
int a=s[i]-'a';
if(tr[p].ne[a]==0) return res;
p=tr[p].ne[a];
res+=tr[p].cnt;
}
return res;
}
ac自动机
#include <bits/stdc++.h>
using namespace std;
const int N=10010,S=55,M=1000010;
struct node{
int ne[26];
int cnt=0;
int fail;
}tr[N*S];
int idx=1;
void insert(string s){
int p=0;
for( int i=0;i<s.size();i++){
int a=s[i]-'a';
if(tr[p].ne[a]==0) tr[p].ne[a]=idx++;
p=tr[p].ne[a];
}
tr[p].cnt++;
}
queue<int>q;
void bfs(){
for( int i=0;i<26;i++){
if(tr[0].ne[i]!=0){
q.push(tr[0].ne[i]);
}
}
while(!q.empty()){
int now=q.front();
q.pop();
for( int i=0;i<26;i++){
int p=tr[now].ne[i];
if(p==0) tr[now].ne[i]=tr[tr[now].fail].ne[i];
//如果是空结点,指向其父节点的回溯结点
else{
tr[p].fail=tr[tr[now].fail].ne[i];
//如果结点不空,那么就把回溯指针指向其父节点回族结点的相同字母结点
q.push(p);
}
}
}
}
int main(){
int t;
cin>>t;
while(t--){
memset(tr,0,sizeof tr);
idx=1;
int n;
cin>>n;
for( int i=0;i<n;i++){
string s;
cin>>s;
insert(s);
}
bfs();
int res=0;
string s;
cin>>s;
for( int i=0,j=0;i<s.size();i++){
int a=s[i]-'a';
j=tr[j].ne[a];
int p=j;
while(p){
res+=tr[p].cnt;
tr[p].cnt=0;
p=tr[p].fail;
}
}
cout<<res<<endl;
}
}
可持久化线段树
绝大多数情况下,可耻就化线段树用于维护某个区间内数字出现的个数,线段树开在下标上,即权值线段树
权值线段树不一定离散化,即使没有的点就空着,留给0结点
相当于线段树套线段树
# 可持久化线段树
```cpp
#include <bits/stdc++.h>
using namespace std;
struct num
{
int val, pos;
int val2;
} a[200100];
int b[200100];
struct node
{
int val;
node *l, *r;
node()
{
l = NULL;
r = NULL;
val = 0;
}
};
node *h[200100];
node *build(int l, int r)
{ //建立空树
node *Newnode = new node;
if (l == r)
return Newnode;
int mid = (l + r) / 2;
Newnode->l = build(l, mid);
Newnode->r = build(mid + 1, r);
return Newnode;
}
node *insert(node *old, int l, int r, int val, int pos)
{
node *Newnode = new node;
*Newnode = *old; //将老树的数据拷贝,注意指针引用
if (l == r)
{
Newnode->val += val;
return Newnode;
}
int mid = (l + r) / 2;
if (pos <= mid)
Newnode->l = insert(Newnode->l, l, mid, 1, pos);
else
Newnode->r = insert(Newnode->r, mid + 1, r, 1, pos);
Newnode->val = Newnode->l->val + Newnode->r->val;
return Newnode;
}
int q(node *h1, node *h2, int l, int r, int k)
{
if (l == r)
return l;
int mid = (l + r) / 2;
int cnt = h1->l->val - h2->l->val;
// cout<<l<<" "<<r<<" "<<mid<<" "<<cnt<<" "<<k<<endl;
//求区间第k大的数,而且最终返回的是左区间的值,
if (k <= cnt)
return q(h1->l, h2->l, l, mid, k);
//左区间已经有k个值,那么直接在左区间寻找即可
else
//左区间不足k个值,还差k - cnt个值,在有区间中再找k - cnt个值
return q(h1->r, h2->r, mid + 1, r, k - cnt);
}
int cmp(num a, num b)
{
return a.val < b.val;
}
int main()
{
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i++)
{
cin >> a[i].val;
a[i].pos = i;
}
sort(a + 1, a + 1 + n, cmp);
for (int i = 1; i <= n; i++)
{
b[a[i].pos] = i;
}
h[0] = build(0, n);
for (int i = 1; i <= n; i++)
{
h[i] = insert(h[i - 1], 1, n, 1, b[i]);
}
while (m--)
{
int l, r, k;
scanf("%d%d%d", &l, &r, &k);
printf("%d\n", a[q(h[r], h[l - 1], 1, n, k)].val);
}
return 0;
}
treap 树
先介绍使用multiset模拟平衡树
multiset<int>se;
se.insert(x);
向平衡树中插入x
auto it=se.find(x);
se.erase(it);
在平衡树中删除一个x
auto minn=se.begin();
minn指向平衡树中的最小值
auto maxx=se.rbegin();
maxx指向平衡树中的最大值
auto it=se.find(x);
it--;
(查找一个在树中存在的数的前驱)
it指向x不等于x的前驱
不等于x的后继结点需要些循环
auto it=se.find(x);
while(*it==x) it++;
lower_bound(x)找到大于等于x的数,返回迭代器,x不一定存在
upper_bound(x)找到严格大于x的数,返回迭代器,x不一定存在
#include <bits/stdc++.h>
using namespace std;
const int N=100100,inf=1e8;
int n;
struct node
{
int l,r;
int key,val;
int cnt ,size;
//size用来寻找排名
}tr[N];
int root ,idx;
void pushup( int p){
tr[p].size=tr[tr[p].l].size+tr[tr[p].r].size+tr[p].cnt;
//维护结点的size等于两个子树的size+自己的数值个数
}
int get_node( int key){
tr[++idx].key=key;
tr[idx].val=rand();
tr[idx].cnt=tr[idx].size=1;
return idx;
}
void zig( int &p) //右旋
//这里要修改p的值,要传递引用
{
int q=tr[p].l;
tr[p].l=tr[q].r,tr[q].r=p,p=q;
pushup(tr[p].r),pushup(p);
}
void zag ( int &p){
int q=tr[p].r;
tr[p].r=tr[q].l,tr[q].l=p,p=q;
pushup(tr[p].l),pushup(p);
}
void build(){
get_node(-inf),get_node(inf);
root=1,tr[1].r=2;
pushup(root);
if(tr[1].val<tr[2].val) zag(root);
//两个哨兵一个位于树的最左边,另一个位于树的最右边
}
void insert( int &p,int key){
if(!p) p=get_node(key);
else if(tr[p].key==key) tr[p].cnt++;
else if(tr[p].key>key){
insert(tr[p].l,key);
if(tr[tr[p].l].val>tr[p].val) zig(p);
//先把新节点当作叶子结点插入,然后旋转到适当的位置
}
else{
insert(tr[p].r,key);
if(tr[tr[p].r].val>tr[p].val) zag(p);
}
pushup(p);
}
void remove( int &p,int key){
//将结点转到叶子结点再删除
if(!p)return ;
if(tr[p].key==key){//要删除当前结点
if(tr[p].cnt>1) tr[p].cnt--;
else if(tr[p].l||tr[p].r){
//存在子节点
if(!tr[p].r||tr[tr[p].l].val>tr[tr[p].r].val){
//只存在左儿子或者左val大于右val
zig(p);//右旋
remove (tr[p].r,key);
}
else{
zag(p);
remove (tr[p].l,key);
}
}
else p=0;//如果是叶子结点,让父节点指向该节点的指针等于0
}
else if(tr[p].key>key) remove(tr[p].l,key);
else remove(tr[p].r,key);
pushup(p);
}
int get_rank_by_key( int p,int key){
//通过数值找排名
if(!p) return 0;
if(tr[p].key==key) return tr[tr[p].l].size+1;
if(tr[p].key>key)return get_rank_by_key(tr[p].l,key);
else
return tr[tr[p].l].size+tr[p].cnt+get_rank_by_key(tr[p].r,key);
}
int get_key_by_rank(int p,int rank){
//通过排名找数值
if(!p) return inf;
if(tr[tr[p].l].size>=rank) return get_key_by_rank(tr[p].l,rank);
if(tr[tr[p].l].size+tr[p].cnt>=rank)
return tr[p].key;
//左子树个数不够,但是加上当前结点数值又多了
else
return get_key_by_rank(tr[p].r,rank-tr[tr[p].l].size-tr[p].cnt);
//左子树加上当前结点也不够,就在右子树找一些结点补上
}
int get_prev( int p,int key){
if(!p) return -inf;
if(tr[p].key>=key) return get_prev(tr[p].l,key);
//右子树一定没有,在左子树找
return max(tr[p].key,get_prev(tr[p].r,key));
}
int get_next(int p,int key){
if(!p) return inf;
if(tr[p].key<=key) return get_next(tr[p].r,key);
return min(tr[p].key,get_next(tr[p].l,key));
}
int main(){
build();
int n;
scanf("%d",&n);
while(n--){
int op,x;
scanf("%d%d",&op,&x);
if(op==1) insert(root,x);
else if(op==2) remove(root,x);
else if(op==3) printf("%d\n",get_rank_by_key(root,x)-1);
else if(op==4) printf("%d\n",get_key_by_rank(root,x+1));
else if(op==5) printf("%d\n",get_prev(root,x));
else printf("%d\n",get_next(root,x));
}
return 0;
}