P3850 [TJOI2007] 书架
[TJOI2007] 书架
题目描述
Knuth 先生家里有个精致的书架,书架上有 \(N\) 本书,如今他想学到更多的知识,于是又买来了 \(M\) 本不同的新书。现在他要把新买的书依次插入到书架中,他已经把每本书要插入的位置标记好了,并且相应的将它们放好。由于 Knuth 年龄已大,过几天他已经记不清某些位置上放的到底是什么书了,请问你能帮助他吗?
对于 \(100\%\) 的数据,\(1 \leqslant N \leqslant 200\), \(1 \leqslant M \leqslant 10^5\), \(1 \leqslant Q \leqslant 10^4\). 保证输入合法
Solution:
最近线段树写吐了捏,来写平衡树捏,平衡树好写捏。
其实我没太搞懂这是怎么评到紫的,我们只需要按照题面维护一颗平衡树,然后对于每个插入操作 (x,k),将 FHQ-Treap 的前 k 个节点splite出来,然后再按照 (L,x,R) 的顺序 merge 回去。
查询也是直接 splite 然后输出,再 merge
感觉没什么难的,几乎没有任何思维难度。
感觉这个甚至比平衡树模板简单
点击此处与妙妙思维平衡题一决高下
Code:
#include<bits/stdc++.h>
const int N=1.1e5+5;
int rd(){return rand()*rand()+1;}
using namespace std;
//FHQ-Treap:
int n,m,cnt,rt,q;
struct FHQ_Treap{struct Tree{int ls,rs,val,siz,rnd;}t[N];void pushup(int x){t[x].siz=t[t[x].ls].siz+t[t[x].rs].siz+1;}int Node(){cnt++;t[cnt]={0,0,cnt,1,rd()};return cnt;}void splite(int x,int &a,int &b,int k){if(!x){a=b=0;return;}int tmp=t[t[x].ls].siz+1;if(tmp<=k){a=x;splite(t[x].rs,t[a].rs,b,k-tmp);pushup(a);}else{b=x;splite(t[x].ls,a,t[b].ls,k);pushup(b);}}int merge(int x,int y){if(!x||!y)return x|y;if(t[x].rnd<t[y].rnd){t[x].rs=merge(t[x].rs,y);pushup(x);return x;}else{t[y].ls=merge(x,t[y].ls);pushup(y);return y;}}
}T;
string s[N];
void work()
{cin>>n;for(int i=1;i<=n;i++){cin>>s[i];rt=T.merge(rt,T.Node());}cin>>m;for(int i=1,k,a,b;i<=m;i++){cin>>s[T.Node()];cin>>k;T.splite(rt,a,b,k);rt=T.merge(T.merge(a,cnt),b);}cin>>q;for(int i=1,k,a,b,c;i<=q;i++){cin>>k;T.splite(rt,a,b,k+1);T.splite(a,a,c,k);cout<<s[T.t[c].val]<<"\n";rt=T.merge(T.merge(a,c),b);}
}
int main()
{//freopen("P3850.in","r",stdin);freopen("P3850.out","w",stdout);ios_base::sync_with_stdio(false);cin.tie(0); cout.tie(0);work();return 0;
}