《由于
S
p
l
a
y
Splay
Splay 码量太大了所以转战
T
r
e
a
p
Treap
Treap 的美好故事》
T
r
e
a
p
Treap
Treap 真香,感觉
S
p
l
a
y
Splay
Splay,替罪羊树和
W
B
L
T
WBLT
WBLT 都用
T
r
e
a
p
Treap
Treap 替代就可以了 (???)
FHQ-Treap
FHQ-Treap | by zhy137036 又叫无旋
T
r
e
a
p
Treap
Treap 像
T
r
e
a
p
Treap
Treap 一样,每个点有一个随机权值,根据随机权值做大根堆 / 小根堆来保证复杂度 又像
S
p
l
a
y
Splay
Splay 一样,每次插入 / 删除数字都把树分裂,再合并。
#include<bits/stdc++.h>#definels(son[p][0])#definers(son[p][1])structpair{int a,b;pair(int a_=0,int b_=0){ a=a_; b=b_;}};intread(){int ans=0;char ch=getchar();while(ch>'9'||ch<'0')ch=getchar();while(ch<='9'&&ch>='0'){
ans=ans*10+ch-'0';
ch=getchar();}return ans;}constint MAX =2e6+50;int key[MAX],wei[MAX],sz[MAX],son[MAX][2];int cnt,root;inlinevoidpush_up(int p){
sz[p]= sz[ls]+ sz[rs]+1;}// 分成两个树,左边树权值的都 < k, 右边数都 >= k// 返回值:两个树的根节点inline pair split(int p,int k){if(!p)returnpair(0,0);if(key[p]< k){
pair t =split(rs,k);
rs = t.a;push_up(p);returnpair(p,t.b);}else{
pair t =split(ls,k);
ls = t.b;push_up(p);returnpair(t.a,p);}}// 根据随机权值(小根堆),合并两棵树// 返回合并后的根inlineintmerge(int p,int v){if(!p ||!v)return p | v;if(wei[p]< wei[v]){
rs =merge(rs,v);push_up(p);return p;}else{
son[v][0]=merge(p,son[v][0]);push_up(v);return v;}}// 新增一个单节点的树 z ,分裂树成 x,y,合并 x,y,zinlinevoidinsert(int k){
key[++cnt]= k; wei[cnt]=rand(); sz[cnt]=1;
pair t =split(root,k);
root =merge(merge(t.a,cnt),t.b);}// 分裂树成 x,y,z ,x 权值 < k ,y 权值 = k,z 权值 > k// 然后合并 y 的子树 ,相当于删去了根inlinevoiderase(int k){
pair x,y;
x =split(root,k);
y =split(x.b,k+1);
y.a =merge(son[y.a][0],son[y.a][1]);
root =merge(x.a,merge(y.a,y.b));}// 查询 k 的排名,分裂获得左子树大小 + 1inlineintget_rk_from_node(int k){int res;
pair t =split(root,k);
res = sz[t.a]+1;
root =merge(t.a,t.b);return res;}// 查询排名 k 的结点inlineintget_node_by_rk(int k){int p = root;while(p){if(k == sz[ls]+1)return key[p];if(k <= sz[ls]) p = ls;else{ k -= sz[ls]+1; p = rs;}}}// 找前驱 后继intlst(int k){returnget_node_by_rk(get_rk_from_node(k)-1);}intnxt(int k){returnget_node_by_rk(get_rk_from_node(k+1));}intmain(){int n,m,last =0,ans =0;
n=read(); m=read();for(int i=1;i<=n;i++){int a=read();insert(a);}for(int i=1;i<=m;i++){int o=read(),x; x=read();if(o==1)insert(x^last);if(o==2)erase(x^last);if(o==3){ last=get_rk_from_node(x^last); ans^=last;}if(o==4){ last=get_node_by_rk(x^last); ans^=last;}if(o==5){ last=lst(x^last); ans^=last;}if(o==6){ last=nxt(x^last); ans^=last;}}printf("%d\n",ans);return0;}
01-Trie
原来
01
−
T
r
i
e
01-Trie
01−Trie 也可以做这些插入删除求排名前驱后继的任务 而且代码确实非常好调,但是内存会变成
O
(
n
log
n
)
O(n\log n)
O(nlogn) 【模板】普通平衡树 这题有负数,直接增加一个
N
U
M
=
1
e
7
NUM=1e7
NUM=1e7,让数都是非负整数,然后去插入
#include<cstdio>#include<algorithm>constint maxlog =25;constint MAXN =100010;
using namespace std;
namespace trie{int id =2;int ch[MAXN * maxlog][2];int sz[MAXN * maxlog];intnewnode(){
ch[id][0]= ch[id][1]= sz[id]=0;return id++;}voidins(int x,int d){int u =1;for(int i = maxlog -1;i >=0;i--){int v =(x >> i)&1;if(!ch[u][v]) ch[u][v]=newnode();
u = ch[u][v];
sz[u]+= d;}}intkth(int k){int u =1;int x =0;for(int i = maxlog -1;i >=0;i--){if(sz[ch[u][0]]>= k){
u = ch[u][0];}else{
x |=(1<< i);
k -= sz[ch[u][0]];
u = ch[u][1];}}return x;}intnlt(int x){int ans =0;int u =1;for(int i = maxlog -1;i >=0;i--){if((x >> i)&1){
ans += sz[ch[u][0]];
u = ch[u][1];}else{
u = ch[u][0];}}return ans;}voidclear(){
ch[1][0]= ch[1][1]=0;
id =2;}intpre(int x){returnkth(nlt(x));}intnext(int x){returnkth(nlt(x+1)+1);}}constint num =10000000;intmain(){int n;scanf("%d",&n);for(int i =0;i < n;i++){int ord,t;scanf("%d%d",&ord,&t);switch(ord){case1:trie::ins(t + num,1);break;case2:trie::ins(t + num,-1);break;case3:printf("%d\n",trie::nlt(t + num)+1);break;case4:printf("%d\n",trie::kth(t)- num);break;case5:printf("%d\n",trie::pre(t + num)- num);break;case6:printf("%d\n",trie::next(t + num)- num);break;}}return0;}
这题,注意到最后一种操作,一般的数据结构都不能做,莫队会
T
T
T 珂朵莉树的核心操作就是
a
s
s
i
g
n
assign
assign ,就是把连续权值相同的点收缩成一个点 因为数据随机,所以
s
e
t
set
set 里面的点的个数是
O
(
log
n
)
O(\log n)
O(logn) 级别的,所以复杂度有保证 其他的操作都是暴力操作了。
#include<bits/stdc++.h>
using namespace std;
using ll =longlong;structnode{int l,r;
mutable ll val;
bool operator<(const node &a)const{return l<a.l;}node(int L,int R,ll Val):l(L),r(R),val(Val){}node(int L):l(L){}};
set<node> s;
using si = set<node>::iterator;
si split(int pos){
si it = s.lower_bound(node(pos));if(it != s.end()&& it->l==pos)return it;--it;int l=it->l,r=it->r;
ll val = it->val;
s.erase(it);
s.insert(node(l,pos-1,val));return s.insert(node(pos,r,val)).first;}voidassign(int l,int r,ll val){
si itr=split(r+1),itl=split(l);
s.erase(itl,itr);
s.insert(node(l,r,val));}voidadd(int l,int r,ll val){
si itr=split(r+1),itl=split(l);for(si it=itl;it!=itr;++it)
it->val += val;}
ll kth(int l,int r,int k){
si itr=split(r+1),itl=split(l);
vector<pair<ll,int>> v;
v.clear();for(si it=itl;it!=itr;++it)
v.push_back(pair<ll,int>(it->val,it->r-it->l+1));sort(v.begin(),v.end());for(int i=0;i<v.size();++i){
k -= v[i].second;if(k<=0)return v[i].first;}return-1;}
ll qpow(ll a,int b,ll m){
ll t =1ll;
a %= m;while(b){if(b&1) t=(t*a)%m;
a =(a*a)%m;
b>>=1;}return t;}
ll query(int l,int r,int x,int y){
si itr=split(r+1),itl=split(l);
ll res(0);for(si it=itl;it!=itr;++it)
res=(res+(it->r-it->l+1)*qpow(it->val,x,y))%y;return res;}int n, m, vmax;
ll seed;intrnd(){int ret =(int)seed;
seed =(seed *7+13)%1000000007;return ret;}intmain(){scanf("%d%d%lld%d",&n,&m,&seed,&vmax);for(int i =1; i <= n;++i){int a =rnd()% vmax +1;
s.insert(node(i, i,(ll)a));}
s.insert(node(n +1, n +1,0));for(int i =1; i <= m;++i){int l, r, x, y;int op =rnd()%4+1;
l =rnd()% n +1, r =rnd()% n +1;if(l > r)swap(l, r);if(op ==3) x =rnd()%(r - l +1)+1;else x =rnd()% vmax +1;if(op ==4) y =rnd()% vmax +1;if(op ==1)add(l, r,(ll)x);elseif(op ==2)assign(l, r,(ll)x);elseif(op ==3)printf("%lld\n",kth(l, r, x));elseif(op ==4)printf("%lld\n",query(l, r, x,(ll)y));}return0;}