zoukankan      html  css  js  c++  java
  • bzoj3065 带插入区间K小值

    Description

    从前有n只跳蚤排成一行做早操,每只跳蚤都有自己的一个弹跳力a[i]。跳蚤国王看着这些跳蚤国欣欣向荣的情景,感到非常高兴。这时跳蚤国王决定理性愉悦一下,查询区间k小值。他每次向它的随从伏特提出这样的问题: 从左往右第x个到第y个跳蚤中,a[i]第k小的值是多少。
    这可难不倒伏特,他在脑袋里使用函数式线段树前缀和的方法水掉了跳蚤国王的询问。
    这时伏特发现有些跳蚤跳久了弹跳力会有变化,有的会增大,有的会减少。
    这可难不倒伏特,他在脑袋里使用树状数组套线段树的方法水掉了跳蚤国王的询问。(orz 主席树)
    这时伏特发现有些迟到的跳蚤会插入到这一行的某个位置上,他感到非常生气,因为……他不会做了。
    请你帮一帮伏特吧。
    快捷版题意:带插入、修改的区间k小值在线查询。 

    Input

    第一行一个正整数n,表示原来有n只跳蚤排成一行做早操。
    第二行有n个用空格隔开的非负整数,从左至右代表每只跳蚤的弹跳力。
    第三行一个正整数q,表示下面有多少个操作。
    下面一共q行,一共三种操作对原序列的操作:(假设此时一共m只跳蚤)
    1. Q x y k: 询问从左至右第x只跳蚤到从左至右第y只跳蚤中,弹跳力第k小的跳蚤的弹跳力是多少。(1 <= x <= y <= m, 1 <= k <= y - x + 1)
    2. M x val: 将从左至右第x只跳蚤的弹跳力改为val。(1 <= x <= m)
    3. I x val: 在从左至右第x只跳蚤的前面插入一只弹跳力为val的跳蚤。即插入后从左至右第x只跳蚤是我刚插入的跳蚤。(1 <= x <= m + 1)

    为了体现在线操作,设lastAns为上一次查询的时候程序输出的结果,如果之前没有查询过,则lastAns = 0。
    则输入的时候实际是:
    Q _x _y _k ------> 表示 Q _x^lastAns _y^lastAns _k^lastAns
    M _x _val  ------> 表示 M _x^lastAns _val^lastAns
    I _x _val  ------> 表示 I _x^lastAns _val^lastAns
    简单来说就是操作中输入的整数都要异或上一次询问的结果进行解码。

    (祝Pascal的同学早日转C++,就不提供pascal版的描述了。)

    Output

    对于每个询问输出回答,每行一个回答。 

    Sample Input

    10
    10 5 8 28 0 19 2 31 1 22
    30
    I 6 9
    M 1 11
    I 8 17
    M 1 31
    M 6 26
    Q 2 7 6
    I 23 30
    M 31 7
    I 22 27
    M 26 18
    Q 26 17 31
    I 5 2
    I 18 13
    Q 3 3 3
    I 27 19
    Q 23 23 30
    Q 5 13 5
    I 3 0
    M 15 27
    Q 0 28 13
    Q 3 29 11
    M 2 8
    Q 12 5 7
    I 30 19
    M 11 19
    Q 17 8 29
    M 29 4
    Q 3 0 12
    I 7 18
    M 29 27

    Sample Output

    28
    2
    31
    0
    14
    15
    14
    27
    15
    14

    HINT

    此题作为一个小小的研究来搞吧~做法有很多~不知道这题究竟有多少种做法。
    请自觉O(log^2n),我故意卡块状链表,块链A了的请受我深情一拜……
    A掉的同学请在Discuss里面简要说下自己的做法吧~
    原序列长度 <= 35000
    插入个数 <= 35000,修改个数 <= 70000,查询个数 <= 70000  ,0 <= 每时每刻的权值 <= 70000
    由于是OJ上的题,所以数据无梯度。为了防止卡OJ,本题只有4组数据。

    正解:替罪羊树套值域线段树。

    Orzvfk大神,这题真的很劲啊。。似乎正解很多,不过最近学了替罪羊树,就来写写吧。。

    这题带插入,所以树状数组套主席树就不能用了。我们考虑使用平衡树。如果是线段树套平衡树,我们无法保证插入元素的复杂度,所以我们用平衡树套线段树。那么在每个平衡树上我们就要保存它子树上的所有结点,所以这课平衡树不能旋转。那么我们可以使用替罪羊树,因为替罪羊树很好写,而且常数很优秀(为treap党默哀。。)。这题别的都还好,主要是查询的时候,我开始因为找区间找错了然后调了好久。。而且以后写这种码农题+细节题,一定要在每个函数上都做标注,不然一不小心就弄混了。。

    复杂度的话。。一次插入,替罪羊树是O(logn),然后插入值域线段树是O(logn)。复杂度为O(log^2)。一次查询,替罪羊树找区间为O(logn),查询值域线段树为O(logn),复杂度为O(log^2),重构复杂度均摊为O(log^2)。

      1 //It is made by wfj_2048~
      2 #include <algorithm>
      3 #include <iostream>
      4 #include <cstring>
      5 #include <cstdlib>
      6 #include <cstdio>
      7 #include <vector>
      8 #include <cmath>
      9 #include <queue>
     10 #include <stack>
     11 #include <map>
     12 #include <set>
     13 #define M (10000010)
     14 #define Mx (70000)
     15 #define N (75010)
     16 #define il inline
     17 #define RG register
     18 #define ll long long
     19 #define File(s) freopen(s".in","r",stdin),freopen(s".out","w",stdout)
     20 
     21 using namespace std;
     22 
     23 const double alpha=0.75;
     24 
     25 struct node{ int x,v; }q[N];
     26 
     27 int sum[M],ls[M],rs[M],sst[M],rt[N],tp; //值域线段树
     28 int ch[N][2],val[N],sz[N],st[N],Rt,tot,top; //替罪羊树
     29 int ttp,ans;
     30 char s[10];
     31 
     32 il int gi(){
     33     RG int x=0,q=1; RG char ch=getchar(); while ((ch<'0' || ch>'9') && ch!='-') ch=getchar();
     34     if (ch=='-') q=-1,ch=getchar(); while (ch>='0' && ch<='9') x=x*10+ch-48,ch=getchar(); return q*x;
     35 }
     36 
     37 il void recycle(RG int &x){ //回收线段树结点
     38     if (!x) return; sst[++tp]=x;
     39     recycle(ls[x]),recycle(rs[x]);
     40     sum[x]=0,x=0; return;
     41 }
     42 
     43 il void update(RG int &x,RG int l,RG int r,RG int v,RG int fg){ //线段树修改
     44     if (!x) x=sst[tp--]; sum[x]+=fg;
     45     if (l==r){ if (!sum[x]) recycle(x); return; } RG int mid=(l+r)>>1;
     46     v<=mid ? update(ls[x],l,mid,v,fg) : update(rs[x],mid+1,r,v,fg);
     47     if (!sum[x]) recycle(x); return;
     48 }
     49 
     50 il int query(RG int ttp,RG int l,RG int r,RG int k){ //线段树查询
     51     if (l==r) return l; RG int mid=(l+r)>>1,tmp=0;
     52     for (RG int i=1;i<=ttp;++i) tmp+=q[i].v*sum[ls[q[i].x]];
     53     if (k<=tmp){
     54     for (RG int i=1;i<=ttp;++i) q[i].x=ls[q[i].x];
     55     return query(ttp,l,mid,k);
     56     } else{
     57     for (RG int i=1;i<=ttp;++i) q[i].x=rs[q[i].x];
     58     return query(ttp,mid+1,r,k-tmp);
     59     }
     60 }
     61 
     62 il void dfs(RG int x){ //替罪羊树回收
     63     if (ch[x][0]) dfs(ch[x][0]);
     64     st[++top]=x,recycle(rt[x]);
     65     if (ch[x][1]) dfs(ch[x][1]);
     66     sz[x]=ch[x][0]=ch[x][1]=0; return;
     67 }
     68 
     69 il int divide(RG int l,RG int r){ //替罪羊树拍扁
     70     if (l>r) return 0; RG int mid=(l+r)>>1,x=st[mid];
     71     for (RG int i=l;i<=r;++i) update(rt[x],0,Mx,val[st[i]],1);
     72     ch[x][0]=divide(l,mid-1),ch[x][1]=divide(mid+1,r);
     73     sz[x]=sz[ch[x][0]]+sz[ch[x][1]]+1; return x;
     74 }
     75 
     76 il void rebuild(RG int &x){ top=0,dfs(x),x=divide(1,top); return; } //替罪羊树重建
     77 
     78 il int* Insert(RG int &x,RG int k,RG int v){ //替罪羊树插入
     79     if (!x){ val[x=++tot]=v,sz[x]=1,update(rt[x],0,Mx,v,1); return NULL; }
     80     sz[x]++,update(rt[x],0,Mx,v,1); int *p;
     81     if (k<=sz[ch[x][0]]+1) p=Insert(ch[x][0],k,v);
     82     else p=Insert(ch[x][1],k-sz[ch[x][0]]-1,v);
     83     if (max(sz[ch[x][0]],sz[ch[x][1]])>sz[x]*alpha) p=&x;
     84     return p;
     85 }
     86 
     87 il void insert(RG int k,RG int v){
     88     int *p=Insert(Rt,k,v);
     89     if (p) rebuild(*p); return;
     90 }
     91     
     92 il int change(RG int x,RG int k,RG int v){ //修改
     93     update(rt[x],0,Mx,v,1); RG int p;
     94     if (k==sz[ch[x][0]]+1){
     95     update(rt[x],0,Mx,val[x],-1);
     96     p=val[x],val[x]=v; return p;
     97     }
     98     if (k<=sz[ch[x][0]]) p=change(ch[x][0],k,v);
     99     else p=change(ch[x][1],k-sz[ch[x][0]]-1,v);
    100     update(rt[x],0,Mx,p,-1); return p;
    101 }
    102 
    103 il void ask(RG int x,RG int k,RG int fg){ //找区间
    104     if (k==sz[ch[x][0]]) q[++ttp].x=rt[ch[x][0]],q[ttp].v=fg;
    105     else if (k<sz[ch[x][0]]) ask(ch[x][0],k,fg);
    106     else{
    107     q[++ttp].x=rt[x],q[ttp].v=fg;
    108     q[++ttp].x=rt[ch[x][1]],q[ttp].v=-fg;
    109     if (k>sz[ch[x][0]]+1) ask(ch[x][1],k-sz[ch[x][0]]-1,fg);
    110     }
    111     return;
    112 }
    113 
    114 il int Query(RG int l,RG int r,RG int k){ //查询
    115     ttp=0,ask(Rt,r,1); if (l-1) ask(Rt,l-1,-1);
    116     return query(ttp,0,Mx,k);
    117 }
    118 
    119 il void print(RG int x){
    120     if (ch[x][0]) print(ch[x][0]); printf("%d ",val[x]);
    121     if (ch[x][1]) print(ch[x][1]); return;
    122 }
    123 
    124 il void work(){
    125     tot=gi(); RG int x,y,k,v,Q;
    126     for (RG int i=1;i<M;++i) sst[++tp]=i;
    127     for (RG int i=1;i<=tot;++i) val[i]=gi(),st[i]=i;
    128     Rt=divide(1,tot),Q=gi();
    129     for (RG int i=1;i<=Q;++i){
    130     scanf("%s",s);
    131     if (s[0]=='Q') x=gi()^ans,y=gi()^ans,k=gi()^ans,printf("%d
    ",ans=Query(x,y,k));
    132     if (s[0]=='M') x=gi()^ans,v=gi()^ans,change(Rt,x,v);
    133     if (s[0]=='I') x=gi()^ans,v=gi()^ans,insert(x,v);
    134     }
    135     return;
    136 }
    137 
    138 int main(){
    139     File("tree");
    140     work();
    141     return 0;
    142 }
  • 相关阅读:
    构造方法
    封装 private
    局部变量和成员变量区别
    IOC
    Linux端口占用查询命令
    Nginx小白入门实战
    SQL left join right join inner join之间的区别
    IDEA导入maven工程时,不会自动识别怎么办
    Spring中Controller层中的method显示为灰色并且提示method is never used的原因
    查看服务器公网IP
  • 原文地址:https://www.cnblogs.com/wfj2048/p/6504493.html
Copyright © 2011-2022 走看看