输入
第1行:1个正整数n,表示操作数量,100≤n≤200,000
第2..n+1行:可能包含下面3种规则:
1个字母'I',紧接着1个数字k,表示插入一个数字k到树中,1≤k≤1,000,000,000,保证每个k都不相同
1个字母'Q',紧接着1个数字k。表示询问树中不超过k的最大数字
1个字母'D',紧接着2个数字a,b,表示删除树中在区间[a,b]的数。
输出
若干行:每行1个整数,表示针对询问的回答,保证一定有合法的解
- 样例输入
-
6 I 1 I 2 I 3 Q 4 D 2 2 Q 2
- 样例输出
-
3 1
学习了一周多的后缀自动机,暂时搁下,回去学习平衡树。
离学习treap树已经有两周多了,终于开始学splay了,ORZ平衡树。
一查维基百科,发现tarjan还参与发明了splay树,ORZ:“The splay tree was invented by Daniel Sleator and Robert Tarjan in 1985.”
ps:
第一次写,也是抄的模板,加了自己的一些批注。
也做了一点小改动,hihocoder在删去区间[a,b]的时候是先加上点a和b,防止没有这两个点。
而此处的做法是找到最大的x<a,最小的y>b(不能等于!),然后删去x,y之间的,这样就不必加点了。
不过Find(x)函数可以判断是否存在某点,但移到根的不一定是x。
#include<iostream> #include<cstdio> #include<cstdlib> #include<cstring> #include<algorithm> using namespace std; const int maxN=400101; const int inf=2147483647; struct SplayData { int fa,ch[2],key,cnt,size;//cnt是单个节点的数据;size是子树的数据 ,此题不用size; SplayData()//自动初始每一个新建data { ch[0]=ch[1]=0; cnt=0; size=0; } }; struct SplayTree { int cnt; int root; SplayData S[maxN]; SplayTree()//自动初始树 { root=0; cnt=0; Insert(-inf);//插入极大极小值 Insert(inf); } void Insert(int x) { int now=root;//root不一定为0 ; int nowfa=0;//上一个now,fa[now]; while (now!=0&&S[now].key!=x){ nowfa=now; now=S[now].ch[x>S[now].key]; } if (now==0){//节点为空 ,新建节点; cnt++; now=cnt; S[cnt].fa=nowfa;//父关系 S[cnt].cnt=S[cnt].size=1; S[cnt].key=x; if (nowfa!=0) S[nowfa].ch[x>S[nowfa].key]=cnt;//子关系 if (root==0) root=1;//树非空 } else S[now].cnt++;//节点非空 Splay(now,0); return; } bool Find(int x) { if (root==0) return 0;//树为空,肯定找不到 int now=root; while ((S[now].ch[x>S[now].key]!=0)&&(x!=S[now].key)) now=S[now].ch[x>S[now].key]; //nice code!!! Splay(now,0);//不管找到没都把now移到根节点。 if (x!=S[now].key) return 0; return 1; } void Rotate(int x) { int y=S[x].fa; int z=S[y].fa; int k1=S[y].ch[1]==x; int k2=S[z].ch[1]==y; S[z].ch[k2]=x; S[x].fa=z; S[y].ch[k1]=S[x].ch[k1^1]; S[S[x].ch[k1^1]].fa=y; S[x].ch[k1^1]=y; S[y].fa=x; return; } void Splay(const int x,int goal) { while (S[x].fa!=goal) { int y=S[x].fa; int z=S[y].fa; if (z!=goal) ((S[z].ch[0]==y)^(S[y].ch[0]==x))?Rotate(x):Rotate(y);//异则x,同则y Rotate(x); } if (goal==0) root=x; return; } int Next(int x,int opt) { Find(x);//先移‘x’到根 int now=root; if ((S[now].key<x)&&(opt==0)) return now; //对根做处理 if ((S[now].key>x)&&(opt==1)) return now; now=S[now].ch[opt]; while (S[now].ch[opt^1]!=0) now=S[now].ch[opt^1];//沿子树一直找 return now; } void DeleteRange(int l,int r) { // Insert(l);//删去 ?//Insert(r); int prep=Next(l,0);//移到根 int nex=Next(r,1);//移到根的右儿子 Splay(prep,0); Splay(nex,prep); S[nex].ch[0]=0;//删去根的右儿子的左儿子 return; } }; SplayTree SP; int main() { int k,l,r,n; scanf("%d",&n); for (int i=1;i<=n;i++){ char opt[2]; scanf("%s",opt); if (opt[0]=='I'){ scanf("%d",&k); SP.Insert(k); } else if (opt[0]=='Q'){ scanf("%d",&k); if (SP.Find(k)){//恰好找到 printf("%d ",k); continue; } int prep=SP.Next(k,0);//否则 ,找左子树最大。 printf("%d ",SP.S[prep].key); } else if (opt[0]=='D'){ scanf("%d%d",&l,&r); SP.DeleteRange(l,r); } } return 0; }
像这样没有特殊的处理的,基于同样原理的set还是可以乱搞的。
#include<cstdio> #include<cstdlib> #include<cstring> #include<iostream> #include<set> using namespace std; set<int>q; int main() { int n,i,a,b; char opt[3]; scanf("%d",&n); while(n--){ scanf("%s",opt); if(opt[0]=='I'){ scanf("%d",&a); q.insert(a); } else if(opt[0]=='Q'){ scanf("%d",&a); set<int>:: iterator it=q.upper_bound(a); it--; printf("%d ",*it); } else { scanf("%d%d",&a,&b); q.erase(q.lower_bound(a),q.upper_bound(b)); } } return 0; }