Splay(区间翻转)&树套树(Splay+线段树,90分)
study from:
https://tiger0132.blog.luogu.org/slay-notes
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <iostream>
using namespace std;
#define ll long long const double eps=1e-;
const ll inf=1e9;
const ll mod=1e9+;
const int maxn=1e5+; int par[maxn],son[maxn][],siz[maxn],val[maxn],cnt[maxn],root,id; int chk(int x)
{
return son[par[x]][]==x;
} void pushup(int x)
{
siz[x]=siz[son[x][]]+siz[son[x][]]+cnt[x];
} void rotate(int x)
{
int y=par[x];
int z=par[y];
int k=chk(x);
int w=son[x][k^];
son[y][k]=w,par[w]=y;
son[z][chk(y)]=x,par[x]=z;
son[x][k^]=y,par[y]=x;
pushup(y),pushup(x);
} void splay(int x,int goal=)
{
int y,z;
while (par[x]!=goal)
{
y=par[x];
z=par[y];
if (z!=goal)
{
if (chk(x)==chk(y))
rotate(y);
else
rotate(x);
}
rotate(x);
}
if (!goal)
root=x;
} void find(int x)
{
if (!root)
return;
int cur=root;
while (son[cur][x>val[cur]] && x!=val[cur])
cur=son[cur][x>val[cur]];
splay(cur); ///用于找到排名,前继,后继
} void insert(int x)
{
int cur=root,p=;
while (cur && val[cur]!=x)
{
p=cur;
cur=son[cur][x>val[cur]];
}
if (cur)
cnt[cur]++;
else
{
cur=++id;
if (p)
{
son[p][x>val[p]]=cur;
siz[p]++; ///不写也行,在splay(cur)中会更改
}
son[cur][]=son[cur][]=;
val[cur]=x;
par[cur]=p;
cnt[cur]=siz[cur]=;
}
splay(cur); ///保持平衡
} int kth(int k)
{
int cur=root;
while ()
{
if (son[cur][] && k<=siz[son[cur][]])
cur=son[cur][];
else if (k>siz[son[cur][]]+cnt[cur])
{
k-=siz[son[cur][]]+cnt[cur];
cur=son[cur][];
}
else
return cur;
}
} void x_rank(int x)
{
find(x);
if (val[root]>=x) ///根节点所在的值不选用
printf("%d\n",siz[son[root][]]+-); ///最小的点
else
printf("%d\n",siz[son[root][]]+cnt[root]+-); ///最小的点
} int pre(int x)
{
find(x);
if (val[root]<x)
return root;
int cur=son[root][];
while (son[cur][])
cur=son[cur][];
return cur;
} int succ(int x)
{
find(x);
if (val[root]>x)
return root;
int cur=son[root][];
while (son[cur][])
cur=son[cur][];
return cur;
} void remove(int x)
{
int last=pre(x);
int next=succ(x);
splay(last);
splay(next,last);
int del=son[next][]; ///有且只有一个节点
if (cnt[del]>)
{
cnt[del]--;
splay(del);
}
else
son[next][]=; ///删除该节点
pushup(next),pushup(root);
} int main()
{
int t,mode,x;
insert(0xefefefef); ///增加最小的点,pre也许不存在
insert(0x3f3f3f3f); ///增加最大的点,succ也许不存在
scanf("%d",&t);
while (t--)
{
scanf("%d%d",&mode,&x);
if (mode==)
insert(x);
else if (mode==)
remove(x);
else if (mode==)
x_rank(x);
else if (mode==)
printf("%d\n",val[kth(x+)]); ///最小的点
else if (mode==)
printf("%d\n",val[pre(x)]);
else if (mode==)
printf("%d\n",val[succ(x)]);
}
return ;
}
考虑到刚访问的节点在之后有可能很快将再次访问
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <iostream>
using namespace std;
#define ll long long const double eps=1e-;
const ll inf=1e9;
const ll mod=1e9+;
const int maxn=1e5+; int par[maxn],son[maxn][],siz[maxn],val[maxn],cnt[maxn],root,id; int chk(int x)
{
return son[par[x]][]==x;
} void pushup(int x)
{
siz[x]=siz[son[x][]]+siz[son[x][]]+cnt[x];
} void rotate(int x)
{
int y=par[x];
int z=par[y];
int k=chk(x);
int w=son[x][k^];
son[y][k]=w,par[w]=y;
son[z][chk(y)]=x,par[x]=z;
son[x][k^]=y,par[y]=x;
pushup(y),pushup(x);
} void splay(int x,int goal=)
{
int y,z;
while (par[x]!=goal)
{
y=par[x];
z=par[y];
if (z!=goal)
{
if (chk(x)==chk(y))
rotate(y);
else
rotate(x);
}
rotate(x);
}
if (!goal)
root=x;
} void find(int x)
{
if (!root)
return;
int cur=root;
while (son[cur][x>val[cur]] && x!=val[cur])
cur=son[cur][x>val[cur]];
splay(cur);
} void insert(int x)
{
int cur=root,p=;
while (cur && val[cur]!=x)
{
p=cur;
cur=son[cur][x>val[cur]];
}
if (cur)
cnt[cur]++;
else
{
cur=++id;
if (p)
son[p][x>val[p]]=cur;
son[cur][]=son[cur][]=;
val[cur]=x;
par[cur]=p;
cnt[cur]=siz[cur]=;
}
splay(cur);
} int kth(int k)
{
int cur=root;
while ()
{
if (son[cur][] && k<=siz[son[cur][]])
cur=son[cur][];
else if (k>siz[son[cur][]]+cnt[cur])
{
k-=siz[son[cur][]]+cnt[cur];
cur=son[cur][];
}
else
{
splay(cur);
return cur;
}
}
} void x_rank(int x)
{
find(x);
if (val[root]>=x) ///根节点所在的值不选用
printf("%d\n",siz[son[root][]]+-); ///最小的点
else
printf("%d\n",siz[son[root][]]+cnt[root]+-); ///最小的点
} int pre(int x)
{
find(x);
if (val[root]<x)
return root;
int cur=son[root][];
while (son[cur][])
cur=son[cur][];
splay(cur);
return cur;
} int succ(int x)
{
find(x);
if (val[root]>x)
return root;
int cur=son[root][];
while (son[cur][])
cur=son[cur][];
splay(cur);
return cur;
} void remove(int x)
{
int last=pre(x);
int next=succ(x);
splay(last);
splay(next,last);
int del=son[next][]; ///有且只有一个节点
if (cnt[del]>)
{
cnt[del]--;
splay(del);
}
else
son[next][]=; ///删除该节点
pushup(next),pushup(root);
} int main()
{
int t,mode,x;
insert(0x3f3f3f3f);
insert(0xefefefef);
scanf("%d",&t);
while (t--)
{
scanf("%d%d",&mode,&x);
if (mode==)
insert(x);
else if (mode==)
remove(x);
else if (mode==)
x_rank(x);
else if (mode==)
printf("%d\n",val[kth(x+)]); ///最小的点
else if (mode==)
printf("%d\n",val[pre(x)]);
else if (mode==)
printf("%d\n",val[succ(x)]);
}
return ;
}
一些想法和其它的写法
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <iostream>
using namespace std;
#define ll long long const double eps=1e-;
const ll inf=1e9;
const ll mod=1e9+;
const int maxn=1e5+; int par[maxn],son[maxn][],siz[maxn],val[maxn],cnt[maxn],root,id; int max_op; int chk(int x)
{
return son[par[x]][]==x;
} void pushup(int x)
{
siz[x]=siz[son[x][]]+siz[son[x][]]+cnt[x];
} void rotate(int x)
{
int y=par[x];
int z=par[y];
int k=chk(x);
int w=son[x][k^];
son[y][k]=w,par[w]=y;
son[z][chk(y)]=x,par[x]=z;
son[x][k^]=y,par[y]=x;
pushup(y),pushup(x);
} void splay(int x,int goal=)
{
int y,z;
while (par[x]!=goal)
{
y=par[x];
z=par[y];
if (z!=goal)
{
if (chk(x)==chk(y))
rotate(y);
else
rotate(x);
}
rotate(x);
}
if (!goal)
root=x;
} void find(int x)
{
if (!root)
return;
int cur=root;
while (son[cur][x>val[cur]] && x!=val[cur])
cur=son[cur][x>val[cur]];
splay(cur); ///用于找到排名,前继,后继
} void insert(int x)
{
int cur=root,p=;
while (cur && val[cur]!=x)
{
p=cur;
cur=son[cur][x>val[cur]];
}
if (cur)
cnt[cur]++;
else
{
cur=++id;
if (p)
{
son[p][x>val[p]]=cur;
siz[p]++; ///不写也行,在splay(cur)中会更改
}
son[cur][]=son[cur][]=;
val[cur]=x;
par[cur]=p;
cnt[cur]=siz[cur]=;
}
splay(cur); ///保持平衡
} int kth(int k)
{
int cur=root;
while ()
{
if (son[cur][] && k<=siz[son[cur][]])
cur=son[cur][];
else if (k>siz[son[cur][]]+cnt[cur])
{
k-=siz[son[cur][]]+cnt[cur];
cur=son[cur][];
}
else
return cur;
}
} int x_rank(int x)
{
find(x); ///比x小的数目 +1
if (val[root]>=x) ///根节点所在的值不选用
return siz[son[root][0]]+1; ///最小的点
else
return siz[son[root][0]]+cnt[root]+1; ///最小的点
} /**
超时
max_op=49999 下方的
1 1
1 2
1 3
...
的读入方式,会使产生一条长长的链 求深度大的点,
splay操作,
会使深度趋于平衡。 在这里没有splay操作,
每次处理的都是长链
**/
//int x_rank(int x)
//{
// int op=0;
// int cur=root,sum=0;
// while (!(son[cur][0]==0 && son[cur][1]==0) && val[cur]!=x)
// {
// if (x<val[cur])
// cur=son[cur][0];
// else
// {
// sum+=siz[son[cur][0]]+cnt[cur];
// cur=son[cur][1];
// }
// op++;
// }
// max_op=max(max_op,op);
// return sum+siz[son[cur][0]]+1;
//} int pre(int x)
{
find(x);
if (val[root]<x)
return root;
int cur=son[root][];
while (son[cur][])
cur=son[cur][];
return cur;
} ///also ok
//int pre(int x)
//{
// int num=x_rank(x);///里面包含find函数
// return kth(num-1);
//} int succ(int x)
{
find(x);
if (val[root]>x)
return root;
int cur=son[root][];
while (son[cur][])
cur=son[cur][];
return cur;
} ///also ok
//int succ(int x)
//{
// int num=x_rank(x);
// if (val[root]==x)
// return kth(num+cnt[root]);
// else
// return kth(num);
//} void remove(int x)
{
int last=pre(x);
int next=succ(x);
splay(last);
splay(next,last);
int del=son[next][]; ///有且只有一个节点
if (cnt[del]>)
{
cnt[del]--;
splay(del);
}
else
son[next][]=; ///删除该节点
pushup(next),pushup(root);
} int main()
{
int t,mode,x;
insert(0xefefefef); ///增加最小的点,pre也许不存在
insert(0x3f3f3f3f); ///增加最大的点,succ也许不存在
scanf("%d",&t);
while (t--)
{
scanf("%d%d",&mode,&x);
if (mode==)
insert(x);
else if (mode==)
remove(x);
else if (mode==)
printf("%d\n",x_rank(x)-);
else if (mode==)
printf("%d\n",val[kth(x+)]); ///最小的点
else if (mode==)
printf("%d\n",val[pre(x)]);
else if (mode==)
printf("%d\n",val[succ(x)]);
} // printf("max_op=%d",max_op);
return ;
}
/*
4
1 1
3 1
5 1
6 1 100
1 1
1 2
1 3
1 4
1 5
1 6
1 7
1 8
1 9
1 10
3 1
3 10
*/
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <iostream>
using namespace std;
#define ll long long const double eps=1e-;
const ll inf=1e9;
const ll mod=1e9+;
const int maxn=1e5+; int vir1=0xefefefef,vir2=0x3f3f3f3f;
int par[maxn],son[maxn][],val[maxn],siz[maxn],rev[maxn],id,root,n;
bool pr; int chk(int x)
{
return son[par[x]][]==x;
} void pushup(int x)
{
siz[x]=siz[son[x][]]+siz[son[x][]]+;
} void rotate(int x)
{
int y=par[x];
int z=par[y];
int k=chk(x);
int w=son[x][k^];
son[y][k]=w,par[w]=y;
son[z][chk(y)]=x,par[x]=z;
son[x][k^]=y,par[y]=x;
pushup(y),pushup(x);
} void splay(int x,int goal=)
{
int y,z;
while (par[x]!=goal)
{
y=par[x];
z=par[y];
if (z!=goal)
{
if (chk(x)==chk(y))
rotate(y);
else
rotate(x);
}
rotate(x);
}
if (!goal)
root=x;
} ///编号从小到大,当前编号比之前读入的数的编号大
void insert(int x) ///数值
{
int cur=root,p=;
while (cur)
{
p=cur;
cur=son[cur][];
} cur=++id;
if (p)
{
son[p][]=cur;
siz[p]++;
}
son[cur][]=son[cur][]=;
par[cur]=p;
val[cur]=x;
siz[cur]=;
splay(cur);
} void pushdown(int x)
{
if (rev[x])
{
swap(son[x][],son[x][]);
rev[son[x][]]^=;
rev[son[x][]]^=;
rev[x]=;
}
} int kth(int k)
{
int cur=root;
while ()
{
pushdown(cur);
if (son[cur][] && k<=siz[son[cur][]])
cur=son[cur][];
else if (k>siz[son[cur][]]+)
{
k-=siz[son[cur][]]+;
cur=son[cur][];
}
else
return cur;
}
} void reverse(int l,int r)
{
int x=kth(l-),y=kth(r+);
splay(x);
splay(y,x);
rev[son[y][]]^=; ///interval [l,r], between l-1 and r+1
} void print(int x)
{
pushdown(x);
if (son[x][])
print(son[x][]);
if (x!= && x!=n+) ///smallest and largest number
{
if (!pr)
pr=;
else
printf(" ");
printf("%d",val[x]);
}
if (son[x][])
print(son[x][]);
} int main()
{
int t,i,x,y;
scanf("%d%d",&n,&t);
insert(vir1); ///smallest number, for l-1
for (i=;i<=n;i++)
insert(i);
insert(vir2); ///largest number, for r+1
while (t--)
{
scanf("%d%d",&x,&y);
reverse(x+,y+); ///contain virtual smallest number
}
print(root);
return ;
}
/*
5 0 5 1
1 5
*/
P2042 [NOI2005]维护数列
见 https://tiger0132.blog.luogu.org/slay-notes
线段树套Splay
每个线段树的点区间有一个Splay。
每个点(a[1]~a[n])在log(n)个线段树的点区间中。
动态开点。
相比动态主席树的优势:
空间nlogn
时间复杂度:
参见https://www.cnblogs.com/LadyLex/p/8006478.html,讲得挺好
(图片来自https://www.cnblogs.com/LadyLex/p/8006478.html)
(图片来自https://www.luogu.org/problemnew/solution/P3380)
既然是区间第k大的时间复杂度是
log(n)*log(n)*log(value_range),
那么肯定会超时才对啊……
只有其它方法才能过……
或者是实际跑起来,远没有到达理论的时间复杂度……
估计是数据没有造到极限,
而我的代码常数太高了……
原来开O2优化(提交时选择)……
需要添加
#pragma GCC optimize(2)
调试时去掉这句话
否则有些变量显示optimized out,无法看到数值
也许更快的方法:
zkw线段树,树状数组
90分
求kth,记录修改的位置,之后不用每次求修改的位置(都是一样的)
#pragma GCC optimize(2)
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <iostream>
using namespace std;
#define ll long long /**
log(n)=16
n*log(n)*log(n)=12800000
**/ const double eps=1e-;
const ll mod=1e9+;
const int maxn=5e4+;
const int maxf=maxn<<;
const int maxg=maxn*+maxf*;
///n * log(n)[larger] (每个点在log(n)个区间中出现) + 每个区间加上两个虚拟点(最小、最大) struct node
{
int l,r,b;
}f[maxf]; int par[maxg],son[maxg][],val[maxg],cnt[maxg],siz[maxg];
int a[maxn],id;
int minv=,maxv=1e8,vir1=-,vir2=;
int cal_num[maxn],cnt_cal_num; int chk(int x)
{
return son[par[x]][]==x;
} void pushup(int x)
{
siz[x]=siz[son[x][]]+siz[son[x][]]+cnt[x];
} void rotate(int x)
{
int y=par[x];
int z=par[y];
int k=chk(x);
int w=son[x][k^];
son[y][k]=w,par[w]=y;
son[z][chk(y)]=x,par[x]=z;
son[x][k^]=y,par[y]=x;
pushup(y),pushup(x);
} void splay(int &root,int x,int goal=)
{
int y,z;
while (par[x]!=goal)
{
y=par[x];
z=par[y];
if (z!=goal)
{
if (chk(x)==chk(y))
rotate(y);
else
rotate(x);
}
rotate(x);
}
if (!goal)
root=x;
} void find(int &root,int x)
{
if (!root)
return;
int cur=root;
while (son[cur][x>val[cur]] && x!=val[cur])
cur=son[cur][x>val[cur]];
splay(root,cur);
} int pre(int &root,int x)
{
find(root,x);
if (val[root]<x)
return root;
int cur=son[root][];
while (son[cur][])
cur=son[cur][];
return cur;
} int succ(int &root,int x)
{
find(root,x);
if (val[root]>x)
return root;
int cur=son[root][];
while (son[cur][])
cur=son[cur][];
return cur;
} void insert(int &root,int x)
{
int cur=root,p=;
while (cur && val[cur]!=x)
{
p=cur;
cur=son[cur][x>val[cur]];
}
if (cur)
cnt[cur]++;
else
{
cur=++id;
if (p)
{
son[p][x>val[p]]=cur;
siz[p]++;
}
son[cur][]=son[cur][]=;
val[cur]=x;
par[cur]=p;
cnt[cur]=siz[cur]=;
}
splay(root,cur);
} void remove(int &root,int x)
{
int last=pre(root,x);
int next=succ(root,x);
splay(root,last);
splay(root,next,last);
int del=son[next][];
if (cnt[del]>)
{
cnt[del]--;
splay(root,del);
}
else
son[next][]=;
pushup(next),pushup(root);
} //int kth(int &root,int k)
//{
// int cur=root;
// while (1)
// {
// if (son[cur][0] && k<=siz[son[cur][0]])
// cur=son[cur][0];
// else if (k>siz[son[cur][0]]+cnt[cur])
// {
// k-=siz[son[cur][0]]+cnt[cur];
// cur=son[cur][1];
// }
// else
// return cur;
// }
//} int x_rank(int &root,int x)
{
find(root,x);
if (val[root]>=x)
return siz[son[root][]];
else
return siz[son[root][]]+cnt[root];
} void seg_insert(int ind,int l,int r,int x,int y)
{
///也可以build()函数,初始化
if (!f[ind].b)
{
insert(f[ind].b,vir1); ///根节点编号为0
f[ind].b=id; ///根节点编号为id
insert(f[ind].b,vir2);
}
insert(f[ind].b,y);
if (l==r)
return;
int m=(l+r)>>;
if (x<=m)
seg_insert(ind<<,l,m,x,y);
else
seg_insert(ind<<|,m+,r,x,y);
} void seg_remove(int ind,int l,int r,int x,int y)
{
remove(f[ind].b,y);
if (l==r)
return;
int m=(l+r)>>;
if (x<=m)
seg_remove(ind<<,l,m,x,y);
else
seg_remove(ind<<|,m+,r,x,y);
} void cal_query_rank(int ind,int l,int r,int x,int y)
{
if (x<=l && r<=y)
{
cal_num[++cnt_cal_num]=ind;
return;
}
int m=(l+r)>>;
if (x<=m)
cal_query_rank(ind<<,l,m,x,y);
if (m<y)
cal_query_rank(ind<<|,m+,r,x,y);
} int query_rank(int ind,int l,int r,int x,int y,int z)
{
if (x<=l && r<=y)
return x_rank(f[ind].b,z)-; ///减去最小虚拟点 int m=(l+r)>>,sum=;
if (x<=m)
sum+=query_rank(ind<<,l,m,x,y,z);
if (m<y)
sum+=query_rank(ind<<|,m+,r,x,y,z);
return sum;
} int query_pre(int ind,int l,int r,int x,int y,int z)
{
if (x<=l && r<=y)
return val[pre(f[ind].b,z)];
int m=(l+r)>>,re=vir1;
if (x<=m)
re=max(re,query_pre(ind<<,l,m,x,y,z));
if (m<y)
re=max(re,query_pre(ind<<|,m+,r,x,y,z));
return re;
} int query_succ(int ind,int l,int r,int x,int y,int z)
{
if (x<=l && r<=y)
return val[succ(f[ind].b,z)];
int m=(l+r)>>,re=vir2;
if (x<=m)
re=min(re,query_succ(ind<<,l,m,x,y,z));
if (m<y)
re=min(re,query_succ(ind<<|,m+,r,x,y,z));
return re;
} int main()
{
int n,m,i,l,r,k,mode,x,L,R,mid;
scanf("%d%d",&n,&m);
for (i=;i<=n;i++)
{
scanf("%d",&x);
seg_insert(,,n,i,x);
a[i]=x;
}
for (int M=;M<=m;M++)
{
scanf("%d",&mode);
if (mode==)
scanf("%d%d",&l,&k);
else
scanf("%d%d%d",&l,&r,&k);
if (mode==)
///log(n)*log(n)
printf("%d\n",query_rank(,,n,l,r,k)+); ///比其小的点的数目+1
else if (mode==)
{
///log(value_range[maxv-minv])
///可离散化成log(maxn+maxm),但写起来很复杂
///可记录区间的最小、最大值,但写起来很麻烦,特别是涉及了数字修改 ///每次遍历的内容都是一致的,用cal_query_rank函数记录下来 cnt_cal_num=;
cal_query_rank(,,n,l,r); L=minv,R=maxv;
while (L<=R)
{
mid=(L+R)>>; x=;
for (i=;i<=cnt_cal_num;i++)
x+=x_rank(f[cal_num[i]].b,mid)-; ///f[index].b 会变化 ///log(n)*log(n)
// x=query_rank(1,1,n,l,r,mid)+1; ///
if (x<=k)
L=mid+;
else
R=mid-;
}
printf("%d\n",R);
}
else if (mode==)
{
///log(n)*log(n)
seg_remove(,,n,l,a[l]);
///log(n)*log(n)
seg_insert(,,n,l,k);
a[l]=k;
}
else if (mode==)
///log(n)*log(n)
printf("%d\n",query_pre(,,n,l,r,k));
else
///log(n)*log(n)
printf("%d\n",query_succ(,,n,l,r,k));
}
return ;
}
/*
9 100
4 2 2 1 9 4 0 1 1 1 1 5 1
1 1 5 2
1 1 5 3
1 1 5 4
1 1 5 5 2 1 5 1
2 1 5 2
2 1 5 3
2 1 5 4
2 1 5 5 3 3 0
1 1 5 3
3 3 1
1 1 5 3
3 3 2
1 1 5 3
3 3 3
1 1 5 3
3 3 5
1 1 5 3
3 3 10
1 1 5 3
3 3 2 4 1 5 0
4 1 5 1
4 1 5 2
4 1 5 3
4 1 5 4
4 1 5 5 5 1 5 0
5 1 5 1
5 1 5 2
5 1 5 3
5 1 5 4
5 1 5 9
5 1 5 10 9 100
1 1 1 1 1 1 1 1 1
1 1 10 1
1 1 10 10
1 1 10 5
2 1 10 1
2 1 10 9
2 1 10 5
4 1 10 1
4 1 10 5
5 1 10 1
5 1 10 -5 5 100
1 2 3 2 1
2 1 5 1
2 1 5 2
2 1 5 3
2 1 5 4
2 1 5 5
2 2 5 1
2 2 5 2
2 2 5 3
2 2 5 4
2 3 4 1
2 3 4 2 1
*/
求kth
二分答案,original r-l = 1e8
离散化
1e8->5e4+5e4
次数 log(1e8)->log(5e4+5e4)
反而是80分,估计是排序操作消耗了一段时间
#pragma GCC optimize(2)
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <iostream>
using namespace std;
#define ll long long /**
log(n)=16
n*log(n)*log(n)=12800000
**/ const double eps=1e-;
const ll mod=1e9+;
const int maxn=5e4+;
const int maxm=5e4+;
const int maxf=maxn<<;
const int maxg=maxn*+maxf*;
///n * log(n)[larger] (每个点在log(n)个区间中出现) + 每个区间加上两个虚拟点(最小、最大) int in_n[maxn],in_m[maxm][],new_vir1,new_vir2; struct rec
{
int v,num;
bool operator<(const rec &y) const
{
return v<y.v;
}
}value[maxn+maxm];
int cnt_value,id_value,value_rev[maxn+maxm]; struct node
{
int l,r,b;
}f[maxf]; int par[maxg],son[maxg][],val[maxg],cnt[maxg],siz[maxg];
int a[maxn],id;
int minv=,maxv=1e8,vir1=-,vir2=;
int cal_num[maxn],cnt_cal_num; int chk(int x)
{
return son[par[x]][]==x;
} void pushup(int x)
{
siz[x]=siz[son[x][]]+siz[son[x][]]+cnt[x];
} void rotate(int x)
{
int y=par[x];
int z=par[y];
int k=chk(x);
int w=son[x][k^];
son[y][k]=w,par[w]=y;
son[z][chk(y)]=x,par[x]=z;
son[x][k^]=y,par[y]=x;
pushup(y),pushup(x);
} void splay(int &root,int x,int goal=)
{
int y,z;
while (par[x]!=goal)
{
y=par[x];
z=par[y];
if (z!=goal)
{
if (chk(x)==chk(y))
rotate(y);
else
rotate(x);
}
rotate(x);
}
if (!goal)
root=x;
} void find(int &root,int x)
{
if (!root)
return;
int cur=root;
while (son[cur][x>val[cur]] && x!=val[cur])
cur=son[cur][x>val[cur]];
splay(root,cur);
} int pre(int &root,int x)
{
find(root,x);
if (val[root]<x)
return root;
int cur=son[root][];
while (son[cur][])
cur=son[cur][];
return cur;
} int succ(int &root,int x)
{
find(root,x);
if (val[root]>x)
return root;
int cur=son[root][];
while (son[cur][])
cur=son[cur][];
return cur;
} void insert(int &root,int x)
{
int cur=root,p=;
while (cur && val[cur]!=x)
{
p=cur;
cur=son[cur][x>val[cur]];
}
if (cur)
cnt[cur]++;
else
{
cur=++id;
if (p)
{
son[p][x>val[p]]=cur;
siz[p]++;
}
son[cur][]=son[cur][]=;
val[cur]=x;
par[cur]=p;
cnt[cur]=siz[cur]=;
}
splay(root,cur);
} void remove(int &root,int x)
{
int last=pre(root,x);
int next=succ(root,x);
splay(root,last);
splay(root,next,last);
int del=son[next][];
if (cnt[del]>)
{
cnt[del]--;
splay(root,del);
}
else
son[next][]=;
pushup(next),pushup(root);
} //int kth(int &root,int k)
//{
// int cur=root;
// while (1)
// {
// if (son[cur][0] && k<=siz[son[cur][0]])
// cur=son[cur][0];
// else if (k>siz[son[cur][0]]+cnt[cur])
// {
// k-=siz[son[cur][0]]+cnt[cur];
// cur=son[cur][1];
// }
// else
// return cur;
// }
//} int x_rank(int &root,int x)
{
find(root,x);
if (val[root]>=x)
return siz[son[root][]];
else
return siz[son[root][]]+cnt[root];
} void seg_insert(int ind,int l,int r,int x,int y)
{
///也可以build()函数,初始化
if (!f[ind].b)
{
insert(f[ind].b,new_vir1); ///根节点编号为0,vir1
f[ind].b=id; ///根节点编号为id
insert(f[ind].b,new_vir2); ///vir2
}
insert(f[ind].b,y);
if (l==r)
return;
int m=(l+r)>>;
if (x<=m)
seg_insert(ind<<,l,m,x,y);
else
seg_insert(ind<<|,m+,r,x,y);
} void seg_remove(int ind,int l,int r,int x,int y)
{
remove(f[ind].b,y);
if (l==r)
return;
int m=(l+r)>>;
if (x<=m)
seg_remove(ind<<,l,m,x,y);
else
seg_remove(ind<<|,m+,r,x,y);
} void cal_query_rank(int ind,int l,int r,int x,int y)
{
if (x<=l && r<=y)
{
cal_num[++cnt_cal_num]=ind;
return;
}
int m=(l+r)>>;
if (x<=m)
cal_query_rank(ind<<,l,m,x,y);
if (m<y)
cal_query_rank(ind<<|,m+,r,x,y);
} int query_rank(int ind,int l,int r,int x,int y,int z)
{
if (x<=l && r<=y)
return x_rank(f[ind].b,z)-; ///减去最小虚拟点 int m=(l+r)>>,sum=;
if (x<=m)
sum+=query_rank(ind<<,l,m,x,y,z);
if (m<y)
sum+=query_rank(ind<<|,m+,r,x,y,z);
return sum;
} int query_pre(int ind,int l,int r,int x,int y,int z)
{
if (x<=l && r<=y)
return val[pre(f[ind].b,z)];
int m=(l+r)>>,re=new_vir1;///vir1
if (x<=m)
re=max(re,query_pre(ind<<,l,m,x,y,z));
if (m<y)
re=max(re,query_pre(ind<<|,m+,r,x,y,z));
return re;
} int query_succ(int ind,int l,int r,int x,int y,int z)
{
if (x<=l && r<=y)
return val[succ(f[ind].b,z)];
int m=(l+r)>>,re=new_vir2;///vir2
if (x<=m)
re=min(re,query_succ(ind<<,l,m,x,y,z));
if (m<y)
re=min(re,query_succ(ind<<|,m+,r,x,y,z));
return re;
} int main()
{
int n,m,i,l,r,k,mode,x,L,R,mid;
scanf("%d%d",&n,&m); for (i=;i<=n;i++)
{
scanf("%d",&in_n[i]);
value[++cnt_value]={in_n[i],i};
}
for (i=;i<=m;i++)
{
scanf("%d",&in_m[i][]);
if (in_m[i][]==)
{
scanf("%d%d",&in_m[i][],&in_m[i][]);
value[++cnt_value]={in_m[i][],n+i};
}
else
{
scanf("%d%d%d",&in_m[i][],&in_m[i][],&in_m[i][]);
if (in_m[i][]!=)
value[++cnt_value]={in_m[i][],n+i};
}
}
value[++cnt_value]={vir1,};
value[++cnt_value]={vir2,}; sort(value+,value+cnt_value+);
value[].v=value[].v-;
for (i=;i<=cnt_value;i++)
{
if (value[i].v!=value[i-].v)
value_rev[++id_value]=value[i].v; if (value[i].v==vir1 || value[i].v==vir2)
continue; if (value[i].num<=n)
in_n[value[i].num]=id_value;
else
in_m[value[i].num-n][]=id_value;
}
new_vir1=;
new_vir2=id_value; for (i=;i<=n;i++)
{
// scanf("%d",&x);
x=in_n[i];
seg_insert(,,n,i,x);
a[i]=x;
} for (int M=;M<=m;M++)
{
mode=in_m[M][];
// scanf("%d",&mode);
if (mode==)
l=in_m[M][],k=in_m[M][];
// scanf("%d%d",&l,&k);
else
l=in_m[M][],r=in_m[M][],k=in_m[M][]; ///attention! // scanf("%d%d%d",&l,&r,&k);
if (mode==)
///log(n)*log(n)
printf("%d\n",query_rank(,,n,l,r,k)+); ///比其小的点的数目+1
else if (mode==)
{
///log(value_range[maxv-minv])
///可离散化成log(maxn+maxm),但写起来很复杂
///可记录区间的最小、最大值,但写起来很麻烦,特别是涉及了数字修改 ///每次遍历的内容都是一致的,用cal_query_rank函数记录下来 cnt_cal_num=;
cal_query_rank(,,n,l,r); L=minv,R=maxv;
while (L<=R)
{
mid=(L+R)>>; x=;
for (i=;i<=cnt_cal_num;i++)
x+=x_rank(f[cal_num[i]].b,mid)-; ///f[index].b 会变化 ///log(n)*log(n)
// x=query_rank(1,1,n,l,r,mid)+1; ///
if (x<=k)
L=mid+;
else
R=mid-;
}
printf("%d\n",value_rev[R]); ///
}
else if (mode==)
{
///log(n)*log(n)
seg_remove(,,n,l,a[l]);
///log(n)*log(n)
seg_insert(,,n,l,k);
a[l]=k;
}
else if (mode==)
///log(n)*log(n)
printf("%d\n",value_rev[query_pre(,,n,l,r,k)]);
else
///log(n)*log(n)
printf("%d\n",value_rev[query_succ(,,n,l,r,k)]);
}
return ;
}
/*
9 36
4 2 2 1 9 4 0 1 1 1 1 5 1
1 1 5 2
1 1 5 3
1 1 5 4
1 1 5 5 2 1 5 1
2 1 5 2
2 1 5 3
2 1 5 4
2 1 5 5 3 3 0
1 1 5 3
3 3 1
1 1 5 3
3 3 2
1 1 5 3
3 3 3
1 1 5 3
3 3 5
1 1 5 3
3 3 10
1 1 5 3
3 3 2 4 1 5 0
4 1 5 1
4 1 5 2
4 1 5 3
4 1 5 4
4 1 5 5 5 1 5 0
5 1 5 1
5 1 5 2
5 1 5 3
5 1 5 4
5 1 5 9
5 1 5 10 9 100
1 1 1 1 1 1 1 1 1
1 1 10 1
1 1 10 10
1 1 10 5
2 1 10 1
2 1 10 9
2 1 10 5
4 1 10 1
4 1 10 5
5 1 10 1
5 1 10 -5 5 100
1 2 3 2 1
2 1 5 1
2 1 5 2
2 1 5 3
2 1 5 4
2 1 5 5
2 2 5 1
2 2 5 2
2 2 5 3
2 2 5 4
2 3 4 1
2 3 4 2 1 4
1
5 1 1 -1
5 1 1 10
6 1 1 -1
6 1 1 10 9 1
4 2 2 1 9 4 0 1 1
5 1 5 10
*/
从上到下
第二个:加splay(),考虑'到刚访问的节点在之后有可能很快将再次访问'
第一个:root形参->全局变量+指针(用于修改)
第三个:
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <iostream>
using namespace std;
#define ll long long /**
log(n)=16
n*log(n)*log(n)=12800000
**/ const double eps=1e-;
const ll mod=1e9+;
const int maxn=5e4+;
const int maxf=maxn<<;
const int maxg=maxn*+maxf*;
///n * log(n)[larger] (每个点在log(n)个区间中出现) + 每个区间加上两个虚拟点(最小、最大) struct node
{
int l,r,b;
}f[maxf]; int par[maxg],son[maxg][],val[maxg],cnt[maxg],siz[maxg];
int a[maxn],id;
int minv=,maxv=1e8,vir1=-,vir2=; int chk(int x)
{
return son[par[x]][]==x;
} void pushup(int x)
{
siz[x]=siz[son[x][]]+siz[son[x][]]+cnt[x];
} void rotate(int x)
{
int y=par[x];
int z=par[y];
int k=chk(x);
int w=son[x][k^];
son[y][k]=w,par[w]=y;
son[z][chk(y)]=x,par[x]=z;
son[x][k^]=y,par[y]=x;
pushup(y),pushup(x);
} void splay(int &root,int x,int goal=)
{
int y,z;
while (par[x]!=goal)
{
y=par[x];
z=par[y];
if (z!=goal)
{
if (chk(x)==chk(y))
rotate(y);
else
rotate(x);
}
rotate(x);
}
if (!goal)
root=x;
} void find(int &root,int x)
{
if (!root)
return;
int cur=root;
while (son[cur][x>val[cur]] && x!=val[cur])
cur=son[cur][x>val[cur]];
splay(root,cur);
} int pre(int &root,int x)
{
find(root,x);
if (val[root]<x)
return root;
int cur=son[root][];
while (son[cur][])
cur=son[cur][];
return cur;
} int succ(int &root,int x)
{
find(root,x);
if (val[root]>x)
return root;
int cur=son[root][];
while (son[cur][])
cur=son[cur][];
return cur;
} void insert(int &root,int x)
{
int cur=root,p=;
while (cur && val[cur]!=x)
{
p=cur;
cur=son[cur][x>val[cur]];
}
if (cur)
cnt[cur]++;
else
{
cur=++id;
if (p)
{
son[p][x>val[p]]=cur;
siz[p]++;
}
son[cur][]=son[cur][]=;
val[cur]=x;
par[cur]=p;
cnt[cur]=siz[cur]=;
}
splay(root,cur);
} void remove(int &root,int x)
{
int last=pre(root,x);
int next=succ(root,x);
splay(root,last);
splay(root,next,last);
int del=son[next][];
if (cnt[del]>)
{
cnt[del]--;
splay(root,del);
}
else
son[next][]=;
pushup(next),pushup(root);
} //int kth(int &root,int k)
//{
// int cur=root;
// while (1)
// {
// if (son[cur][0] && k<=siz[son[cur][0]])
// cur=son[cur][0];
// else if (k>siz[son[cur][0]]+cnt[cur])
// {
// k-=siz[son[cur][0]]+cnt[cur];
// cur=son[cur][1];
// }
// else
// return cur;
// }
//} int x_rank(int &root,int x)
{
find(root,x);
if (val[root]>=x)
return siz[son[root][]];
else
return siz[son[root][]]+cnt[root];
} void seg_insert(int ind,int l,int r,int x,int y)
{
///也可以build()函数,初始化
if (!f[ind].b)
{
insert(f[ind].b,vir1); ///根节点编号为0
f[ind].b=id; ///根节点编号为id
insert(f[ind].b,vir2);
}
insert(f[ind].b,y);
if (l==r)
return;
int m=(l+r)>>;
if (x<=m)
seg_insert(ind<<,l,m,x,y);
else
seg_insert(ind<<|,m+,r,x,y);
} void seg_remove(int ind,int l,int r,int x,int y)
{
remove(f[ind].b,y);
if (l==r)
return;
int m=(l+r)>>;
if (x<=m)
seg_remove(ind<<,l,m,x,y);
else
seg_remove(ind<<|,m+,r,x,y);
} int query_rank(int ind,int l,int r,int x,int y,int z)
{
if (x<=l && r<=y)
return x_rank(f[ind].b,z)-; ///减去最小虚拟点
int m=(l+r)>>,sum=;
if (x<=m)
sum+=query_rank(ind<<,l,m,x,y,z);
if (m<y)
sum+=query_rank(ind<<|,m+,r,x,y,z);
return sum;
} int query_pre(int ind,int l,int r,int x,int y,int z)
{
if (x<=l && r<=y)
return val[pre(f[ind].b,z)];
int m=(l+r)>>,re=vir1;
if (x<=m)
re=max(re,query_pre(ind<<,l,m,x,y,z));
if (m<y)
re=max(re,query_pre(ind<<|,m+,r,x,y,z));
return re;
} int query_succ(int ind,int l,int r,int x,int y,int z)
{
if (x<=l && r<=y)
return val[succ(f[ind].b,z)];
int m=(l+r)>>,re=vir2;
if (x<=m)
re=min(re,query_succ(ind<<,l,m,x,y,z));
if (m<y)
re=min(re,query_succ(ind<<|,m+,r,x,y,z));
return re;
} int main()
{
int n,m,i,l,r,k,mode,x,L,R,mid;
scanf("%d%d",&n,&m);
for (i=;i<=n;i++)
{
scanf("%d",&x);
seg_insert(,,n,i,x);
a[i]=x;
}
while (m--)
{
scanf("%d",&mode);
if (mode==)
scanf("%d%d",&l,&k);
else
scanf("%d%d%d",&l,&r,&k);
if (mode==)
///log(n)*log(n)
printf("%d\n",query_rank(,,n,l,r,k)+); ///比其小的点的数目+1
else if (mode==)
{
///log(value_range[maxv-minv])
///可离散化成log(maxn+maxm),但写起来很复杂
L=minv,R=maxv;
while (L<=R)
{
mid=(L+R)>>;
///log(n)*log(n)
x=query_rank(,,n,l,r,mid)+; ///
if (x<=k)
L=mid+;
else
R=mid-;
}
printf("%d\n",R);
}
else if (mode==)
{
///log(n)*log(n)
seg_remove(,,n,l,a[l]);
///log(n)*log(n)
seg_insert(,,n,l,k);
a[l]=k;
}
else if (mode==)
///log(n)*log(n)
printf("%d\n",query_pre(,,n,l,r,k));
else
///log(n)*log(n)
printf("%d\n",query_succ(,,n,l,r,k));
}
return ;
}
/*
9 100
4 2 2 1 9 4 0 1 1 1 1 5 1
1 1 5 2
1 1 5 3
1 1 5 4
1 1 5 5 2 1 5 1
2 1 5 2
2 1 5 3
2 1 5 4
2 1 5 5 3 3 0
1 1 5 3
3 3 1
1 1 5 3
3 3 2
1 1 5 3
3 3 3
1 1 5 3
3 3 5
1 1 5 3
3 3 10
1 1 5 3
3 3 2 4 1 5 0
4 1 5 1
4 1 5 2
4 1 5 3
4 1 5 4
4 1 5 5 5 1 5 0
5 1 5 1
5 1 5 2
5 1 5 3
5 1 5 4
5 1 5 9
5 1 5 10 9 100
1 1 1 1 1 1 1 1 1
1 1 10 1
1 1 10 10
1 1 10 5
2 1 10 1
2 1 10 9
2 1 10 5
4 1 10 1
4 1 10 5
5 1 10 1
5 1 10 -5 */
第二个:
/**
考虑到刚访问的节点在之后有可能很快将再次访问
**/
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <iostream>
using namespace std;
#define ll long long /**
log(n)=16
n*log(n)*log(n)=12800000
**/ const double eps=1e-;
const ll mod=1e9+;
const int maxn=5e4+;
const int maxf=maxn<<;
const int maxg=maxn*+maxf*;
///n * log(n)[larger] (每个点在log(n)个区间中出现) + 每个区间加上两个虚拟点(最小、最大) struct node
{
int l,r,b;
}f[maxf]; int par[maxg],son[maxg][],val[maxg],cnt[maxg],siz[maxg];
int a[maxn],id;
int minv=,maxv=1e8,vir1=-,vir2=; int chk(int x)
{
return son[par[x]][]==x;
} void pushup(int x)
{
siz[x]=siz[son[x][]]+siz[son[x][]]+cnt[x];
} void rotate(int x)
{
int y=par[x];
int z=par[y];
int k=chk(x);
int w=son[x][k^];
son[y][k]=w,par[w]=y;
son[z][chk(y)]=x,par[x]=z;
son[x][k^]=y,par[y]=x;
pushup(y),pushup(x);
} void splay(int &root,int x,int goal=)
{
int y,z;
while (par[x]!=goal)
{
y=par[x];
z=par[y];
if (z!=goal)
{
if (chk(x)==chk(y))
rotate(y);
else
rotate(x);
}
rotate(x);
}
if (!goal)
root=x;
} void find(int &root,int x)
{
if (!root)
return;
int cur=root;
while (son[cur][x>val[cur]] && x!=val[cur])
cur=son[cur][x>val[cur]];
splay(root,cur);
} int pre(int &root,int x)
{
find(root,x);
if (val[root]<x)
return root;
int cur=son[root][];
while (son[cur][])
cur=son[cur][];
splay(root,cur);
return cur;
} int succ(int &root,int x)
{
find(root,x);
if (val[root]>x)
return root;
int cur=son[root][];
while (son[cur][])
cur=son[cur][];
splay(root,cur);
return cur;
} void insert(int &root,int x)
{
int cur=root,p=;
while (cur && val[cur]!=x)
{
p=cur;
cur=son[cur][x>val[cur]];
}
if (cur)
cnt[cur]++;
else
{
cur=++id;
if (p)
{
son[p][x>val[p]]=cur;
siz[p]++;
}
son[cur][]=son[cur][]=;
val[cur]=x;
par[cur]=p;
cnt[cur]=siz[cur]=;
}
splay(root,cur);
} void remove(int &root,int x)
{
int last=pre(root,x);
int next=succ(root,x);
splay(root,last);
splay(root,next,last);
int del=son[next][];
if (cnt[del]>)
{
cnt[del]--;
splay(root,del);
}
else
son[next][]=;
pushup(next),pushup(root);
} //int kth(int &root,int k)
//{
// int cur=root;
// while (1)
// {
// if (son[cur][0] && k<=siz[son[cur][0]])
// cur=son[cur][0];
// else if (k>siz[son[cur][0]]+cnt[cur])
// {
// k-=siz[son[cur][0]]+cnt[cur];
// cur=son[cur][1];
// }
// else
// {
// splay(cur);
// return cur;
// }
// }
//} int x_rank(int &root,int x)
{
find(root,x);
if (val[root]>=x)
return siz[son[root][]];
else
return siz[son[root][]]+cnt[root];
} void seg_insert(int ind,int l,int r,int x,int y)
{
///也可以build()函数,初始化
if (!f[ind].b)
{
insert(f[ind].b,vir1); ///根节点编号为0
f[ind].b=id; ///根节点编号为id
insert(f[ind].b,vir2);
}
insert(f[ind].b,y);
if (l==r)
return;
int m=(l+r)>>;
if (x<=m)
seg_insert(ind<<,l,m,x,y);
else
seg_insert(ind<<|,m+,r,x,y);
} void seg_remove(int ind,int l,int r,int x,int y)
{
remove(f[ind].b,y);
if (l==r)
return;
int m=(l+r)>>;
if (x<=m)
seg_remove(ind<<,l,m,x,y);
else
seg_remove(ind<<|,m+,r,x,y);
} int query_rank(int ind,int l,int r,int x,int y,int z)
{
if (x<=l && r<=y)
return x_rank(f[ind].b,z)-; ///减去最小虚拟点
int m=(l+r)>>,sum=;
if (x<=m)
sum+=query_rank(ind<<,l,m,x,y,z);
if (m<y)
sum+=query_rank(ind<<|,m+,r,x,y,z);
return sum;
} int query_pre(int ind,int l,int r,int x,int y,int z)
{
if (x<=l && r<=y)
return val[pre(f[ind].b,z)];
int m=(l+r)>>,re=vir1;
if (x<=m)
re=max(re,query_pre(ind<<,l,m,x,y,z));
if (m<y)
re=max(re,query_pre(ind<<|,m+,r,x,y,z));
return re;
} int query_succ(int ind,int l,int r,int x,int y,int z)
{
if (x<=l && r<=y)
return val[succ(f[ind].b,z)];
int m=(l+r)>>,re=vir2;
if (x<=m)
re=min(re,query_succ(ind<<,l,m,x,y,z));
if (m<y)
re=min(re,query_succ(ind<<|,m+,r,x,y,z));
return re;
} int main()
{
int n,m,i,l,r,k,mode,x,L,R,mid;
scanf("%d%d",&n,&m);
for (i=;i<=n;i++)
{
scanf("%d",&x);
seg_insert(,,n,i,x);
a[i]=x;
}
while (m--)
{
scanf("%d",&mode);
if (mode==)
scanf("%d%d",&l,&k);
else
scanf("%d%d%d",&l,&r,&k);
if (mode==)
///log(n)*log(n)
printf("%d\n",query_rank(,,n,l,r,k)+); ///比其小的点的数目+1
else if (mode==)
{
///log(value_range[maxv-minv])
///可离散化成log(maxn+maxm),但写起来很复杂
L=minv,R=maxv;
while (L<=R)
{
mid=(L+R)>>;
///log(n)*log(n)
x=query_rank(,,n,l,r,mid)+; ///
if (x<=k)
L=mid+;
else
R=mid-;
}
printf("%d\n",R);
}
else if (mode==)
{
///log(n)*log(n)
seg_remove(,,n,l,a[l]);
///log(n)*log(n)
seg_insert(,,n,l,k);
a[l]=k;
}
else if (mode==)
///log(n)*log(n)
printf("%d\n",query_pre(,,n,l,r,k));
else
///log(n)*log(n)
printf("%d\n",query_succ(,,n,l,r,k));
}
return ;
}
/*
9 100
4 2 2 1 9 4 0 1 1 1 1 5 1
1 1 5 2
1 1 5 3
1 1 5 4
1 1 5 5 2 1 5 1
2 1 5 2
2 1 5 3
2 1 5 4
2 1 5 5 3 3 0
1 1 5 3
3 3 1
1 1 5 3
3 3 2
1 1 5 3
3 3 3
1 1 5 3
3 3 5
1 1 5 3
3 3 10
1 1 5 3
3 3 2 4 1 5 0
4 1 5 1
4 1 5 2
4 1 5 3
4 1 5 4
4 1 5 5 5 1 5 0
5 1 5 1
5 1 5 2
5 1 5 3
5 1 5 4
5 1 5 9
5 1 5 10 9 100
1 1 1 1 1 1 1 1 1
1 1 10 1
1 1 10 10
1 1 10 5
2 1 10 1
2 1 10 9
2 1 10 5
4 1 10 1
4 1 10 5
5 1 10 1
5 1 10 -5 */
第三个:
/**
root
指针
**/
#include <cstdio>
#include <cstdlib>
#include <cmath>
#include <cstring>
#include <string>
#include <algorithm>
#include <iostream>
using namespace std;
#define ll long long /**
log(n)=16
n*log(n)*log(n)=12800000
**/ const double eps=1e-;
const ll mod=1e9+;
const int maxn=5e4+;
const int maxf=maxn<<;
const int maxg=maxn*+maxf*;
///n * log(n)[larger] (每个点在log(n)个区间中出现) + 每个区间加上两个虚拟点(最小、最大) struct node
{
int l,r,b;
}f[maxf]; int par[maxg],son[maxg][],val[maxg],cnt[maxg],siz[maxg];
int a[maxn],id;
int minv=,maxv=1e8,vir1=-,vir2=;
int *splay_value,root; int chk(int x)
{
return son[par[x]][]==x;
} void pushup(int x)
{
siz[x]=siz[son[x][]]+siz[son[x][]]+cnt[x];
} void rotate(int x)
{
int y=par[x];
int z=par[y];
int k=chk(x);
int w=son[x][k^];
son[y][k]=w,par[w]=y;
son[z][chk(y)]=x,par[x]=z;
son[x][k^]=y,par[y]=x;
pushup(y),pushup(x);
} void splay(int x,int goal=)
{
int y,z;
while (par[x]!=goal)
{
y=par[x];
z=par[y];
if (z!=goal)
{
if (chk(x)==chk(y))
rotate(y);
else
rotate(x);
}
rotate(x);
}
if (!goal)
{
*splay_value=x;
root=x;
} } void find(int x)
{
if (!root)
return;
int cur=root;
while (son[cur][x>val[cur]] && x!=val[cur])
cur=son[cur][x>val[cur]];
splay(cur);
} int pre(int x)
{
find(x);
if (val[root]<x)
return root;
int cur=son[root][];
while (son[cur][])
cur=son[cur][];
return cur;
} int succ(int x)
{
find(x);
if (val[root]>x)
return root;
int cur=son[root][];
while (son[cur][])
cur=son[cur][];
return cur;
} void insert(int x)
{
int cur=root,p=;
while (cur && val[cur]!=x)
{
p=cur;
cur=son[cur][x>val[cur]];
}
if (cur)
cnt[cur]++;
else
{
cur=++id;
if (p)
{
son[p][x>val[p]]=cur;
siz[p]++;
}
son[cur][]=son[cur][]=;
val[cur]=x;
par[cur]=p;
cnt[cur]=siz[cur]=;
}
splay(cur);
} void remove(int x)
{
int last=pre(x);
int next=succ(x);
splay(last);
splay(next,last);
int del=son[next][];
if (cnt[del]>)
{
cnt[del]--;
splay(del);
}
else
son[next][]=;
pushup(next),pushup(root);
} //int kth(int &root,int k)
//{
// int cur=root;
// while (1)
// {
// if (son[cur][0] && k<=siz[son[cur][0]])
// cur=son[cur][0];
// else if (k>siz[son[cur][0]]+cnt[cur])
// {
// k-=siz[son[cur][0]]+cnt[cur];
// cur=son[cur][1];
// }
// else
// return cur;
// }
//} int x_rank(int x)
{
find(x);
if (val[root]>=x)
return siz[son[root][]];
else
return siz[son[root][]]+cnt[root];
} void seg_insert(int ind,int l,int r,int x,int y)
{
///也可以build()函数,初始化
if (!f[ind].b)
{
root=f[ind].b;
splay_value=&f[ind].b;
insert(vir1); ///根节点编号为0 f[ind].b=id; ///根节点编号为id
root=f[ind].b;
splay_value=&f[ind].b;
insert(vir2);
}
root=f[ind].b;
splay_value=&f[ind].b;
insert(y);
if (l==r)
return;
int m=(l+r)>>;
if (x<=m)
seg_insert(ind<<,l,m,x,y);
else
seg_insert(ind<<|,m+,r,x,y);
} void seg_remove(int ind,int l,int r,int x,int y)
{
root=f[ind].b;
splay_value=&f[ind].b;
remove(y);
if (l==r)
return;
int m=(l+r)>>;
if (x<=m)
seg_remove(ind<<,l,m,x,y);
else
seg_remove(ind<<|,m+,r,x,y);
} int query_rank(int ind,int l,int r,int x,int y,int z)
{
if (x<=l && r<=y)
{
root=f[ind].b;
splay_value=&f[ind].b;
return x_rank(z)-; ///减去最小虚拟点
}
int m=(l+r)>>,sum=;
if (x<=m)
sum+=query_rank(ind<<,l,m,x,y,z);
if (m<y)
sum+=query_rank(ind<<|,m+,r,x,y,z);
return sum;
} int query_pre(int ind,int l,int r,int x,int y,int z)
{
if (x<=l && r<=y)
{
root=f[ind].b;
splay_value=&f[ind].b;
return val[pre(z)];
}
int m=(l+r)>>,re=vir1;
if (x<=m)
re=max(re,query_pre(ind<<,l,m,x,y,z));
if (m<y)
re=max(re,query_pre(ind<<|,m+,r,x,y,z));
return re;
} int query_succ(int ind,int l,int r,int x,int y,int z)
{
if (x<=l && r<=y)
{
root=f[ind].b;
splay_value=&f[ind].b;
return val[succ(z)];
}
int m=(l+r)>>,re=vir2;
if (x<=m)
re=min(re,query_succ(ind<<,l,m,x,y,z));
if (m<y)
re=min(re,query_succ(ind<<|,m+,r,x,y,z));
return re;
} int main()
{
int n,m,i,l,r,k,mode,x,L,R,mid;
scanf("%d%d",&n,&m);
for (i=;i<=n;i++)
{
scanf("%d",&x);
seg_insert(,,n,i,x);
a[i]=x;
}
while (m--)
{
scanf("%d",&mode);
if (mode==)
scanf("%d%d",&l,&k);
else
scanf("%d%d%d",&l,&r,&k);
if (mode==)
///log(n)*log(n)
printf("%d\n",query_rank(,,n,l,r,k)+); ///比其小的点的数目+1
else if (mode==)
{
///log(value_range[maxv-minv])
///可离散化成log(maxn+maxm),但写起来很复杂
L=minv,R=maxv;
while (L<=R)
{
mid=(L+R)>>;
///log(n)*log(n)
x=query_rank(,,n,l,r,mid)+; ///
if (x<=k)
L=mid+;
else
R=mid-;
}
printf("%d\n",R);
}
else if (mode==)
{
///log(n)*log(n)
seg_remove(,,n,l,a[l]);
///log(n)*log(n)
seg_insert(,,n,l,k);
a[l]=k;
}
else if (mode==)
///log(n)*log(n)
printf("%d\n",query_pre(,,n,l,r,k));
else
///log(n)*log(n)
printf("%d\n",query_succ(,,n,l,r,k));
}
return ;
}
/*
9 100
4 2 2 1 9 4 0 1 1 1 1 5 1
1 1 5 2
1 1 5 3
1 1 5 4
1 1 5 5 2 1 5 1
2 1 5 2
2 1 5 3
2 1 5 4
2 1 5 5 3 3 0
1 1 5 3
3 3 1
1 1 5 3
3 3 2
1 1 5 3
3 3 3
1 1 5 3
3 3 5
1 1 5 3
3 3 10
1 1 5 3
3 3 2 4 1 5 0
4 1 5 1
4 1 5 2
4 1 5 3
4 1 5 4
4 1 5 5 5 1 5 0
5 1 5 1
5 1 5 2
5 1 5 3
5 1 5 4
5 1 5 9
5 1 5 10 9 100
1 1 1 1 1 1 1 1 1
1 1 10 1
1 1 10 10
1 1 10 5
2 1 10 1
2 1 10 9
2 1 10 5
4 1 10 1
4 1 10 5
5 1 10 1
5 1 10 -5 1 100
1 2
1 1 2 1
1 1 2 2
*/
=========================================
AVL树、splay树(伸展树)和红黑树比较
https://blog.csdn.net/u010585135/article/details/41852945
实况:
AVL树时间上较慢,编程竞赛中一般不用AVL树
splay树时间上较为合适,但比红黑树稍慢,如java底层就使用红黑树编写
但由于红黑树编写极为复杂,所以编程比赛上一般不使用红黑树,而使用splay。
由于splay写起来也比较麻烦,所以有时会用其它方法替代splay,如替罪羊树,仙人掌树这些666的方法。
Splay(区间翻转)&树套树(Splay+线段树,90分)的更多相关文章
- 【bzoj3065】带插入区间K小值 替罪羊树套权值线段树
题目描述 从前有n只跳蚤排成一行做早操,每只跳蚤都有自己的一个弹跳力a[i].跳蚤国王看着这些跳蚤国欣欣向荣的情景,感到非常高兴.这时跳蚤国王决定理性愉悦一下,查询区间k小值.他每次向它的随从伏特提出 ...
- BZOJ - 2141 排队 (动态逆序对,区间线段树套权值线段树)
题目链接 交换两个数的位置,只有位于两个数之间的部分会受到影响,因此只需要考虑两个数之间有多少数对a[l]和a[r]产生的贡献发生了变化即可. 感觉像是个带修改的二维偏序问题.(修改点$(x,y)$的 ...
- BZOJ 1901: Zju2112 Dynamic Rankings 区间k大 带修改 在线 线段树套平衡树
之前写线段树套splay数组版..写了6.2k..然后弃疗了.现在发现还是很水的..嘎嘎.. zju过不了,超时. upd:才发现zju是多组数据..TLE一版才发现.然后改了,MLE...手写内存池 ...
- [bzoj3196][Tyvj1730]二逼平衡树_树套树_位置线段树套非旋转Treap/树状数组套主席树/权值线段树套位置线段树
二逼平衡树 bzoj-3196 Tyvj-1730 题目大意:请写出一个维护序列的数据结构支持:查询给定权值排名:查询区间k小值:单点修改:查询区间内定值前驱:查询区间内定值后继. 注释:$1\le ...
- splay区间翻转
原题P3391 [模板]文艺平衡树(Splay) 题目背景 这是一道经典的Splay模板题——文艺平衡树. 题目描述 您需要写一种数据结构(可参考题目标题),来维护一个有序数列,其中需要提供以下操作: ...
- luogu3380/bzoj3196 二逼平衡树 (树状数组套权值线段树)
带修改区间K大值 这题有很多做法,我的做法是树状数组套权值线段树,修改查询的时候都是按着树状数组的规则找出那log(n)个线段树根,然后一起往下做 时空都是$O(nlog^2n)$的(如果离散化了的话 ...
- CF1093E Intersection of Permutations 树状数组套权值线段树
\(\color{#0066ff}{ 题目描述 }\) 给定整数 \(n\) 和两个 \(1,\dots,n\) 的排列 \(a,b\). \(m\) 个操作,操作有两种: \(1\ l_a\ r_a ...
- Dynamic Rankings(树状数组套权值线段树)
Dynamic Rankings(树状数组套权值线段树) 给定一个含有n个数的序列a[1],a[2],a[3]--a[n],程序必须回答这样的询问:对于给定的i,j,k,在a[i],a[i+1],a[ ...
- [BZOJ 3295] [luogu 3157] [CQOI2011]动态逆序对(树状数组套权值线段树)
[BZOJ 3295] [luogu 3157] [CQOI2011] 动态逆序对 (树状数组套权值线段树) 题面 给出一个长度为n的排列,每次操作删除一个数,求每次操作前排列逆序对的个数 分析 每次 ...
- BZOJ2141排队——树状数组套权值线段树(带修改的主席树)
题目描述 排排坐,吃果果,生果甜嗦嗦,大家笑呵呵.你一个,我一个,大的分给你,小的留给我,吃完果果唱支歌,大家 乐和和.红星幼儿园的小朋友们排起了长长地队伍,准备吃果果.不过因为小朋友们的身高有所区别 ...
随机推荐
- JavaSE---多线程---集合类
1.概述 1.1 Java提供的ArrayList.LinkedList.HashMap等都是线程不安全的类,如何解决: 1.1 使用Collections类提供的方法将 其 包装成线程安全类: ...
- Tomcat集群搭建超详细(apache+mod_jk+tomcat)
TOMCAT集群 目录 TOMCAT集群 1 1 集群 1 1.1 什么是集群 1 1.2 集群的特性 1 1.3 集群的分类 1 1.4 TOMCAT集群配置的优缺点 2 1.5 APACHE+TO ...
- Qwidget布局操作之QGridLayout(网格布局)
QMainWindow并没有setLayout()函数,因此不能使用setLayout()函数来设置layout,需要使用间接的方法. 需要做的只是先定义一个QWidget对象,然后使用QMainWi ...
- (18)C++ string和标准模板库
一.stringl类 1.string构造函数 string a1("abc");//初始化字符串 cout<<a1<<endl;//abc , '#'); ...
- 【git】git的内部原理
参考文章:https://zhuanlan.zhihu.com/p/96631135 参考文章:https://marklodato.github.io/visual-git-guide/index- ...
- [转]DrawPrimitive 详解Direct3DDevice8
Direct3DDevice8 函数 05-39 DrawPrimitive 详解 费了好大的劲,终于搞清楚 DirectX 3D 三维图像中 DrawPrimitive 的用法(自嘲:未必). D ...
- 解决:The “https://packagist.laravel-china.org/packages.json” file could not be downloaded
使用composer安装错误提示: The "https://packagist.laravel-china.org/packages.json" file could not b ...
- VS2008中所有快捷键
转载自:http://itfocus.diandian.com/post/2011-09-16/5091994 微软开发环境的可视化界面做的很全面,几乎所有的操作都可以通过可视化界面完成,但是你是否在 ...
- python_模块 hashlib ,configparser, logging
hashlib模块 算法介绍 Python的hashlib提供了常见的摘要算法,如MD5,SHA1等等. 什么是摘要算法呢?摘要算法又称哈希算法.散列算法.它通过一个函数,把任意长度的数据转换为一个长 ...
- 10-vim-选中命令-01-三种选择文本的方式
选择文本(可视模式) 学习复制命令前,应该先学会如何选中要复制的代码. 在vi中要选择文本,需要先使用visual命令切换到可视模式. vi中提供了三种可视模式. 按ESC可以放弃选中,返回到命令模式 ...