• Bzoj3065 带插入区间K小值


    Time Limit: 60 Sec  Memory Limit: 512 MB
    Submit: 3436  Solved: 1103

    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组数据。


    Source

    树 树套树 替罪羊树套权值线段树

    非旋转的平衡树是可以套在别的树外面的。

    用替罪羊树维护序列,替罪羊树上塞一堆权值线段树维护区间内有哪些值。

    查询的时候,把该区间对应的线段树区间都拎出来,一起二分找第k小。

    查询方法学自黄学长

    注意替罪羊树重建的时候,要按照中序遍历回收节点,能省一个排序操作。

    应该用线段树合并来着,然而听说合并写得不够好的话和暴力重建一个速度,那我就直接暴力重建了

    刷了一页WA

      1 /*by SilverN*/
      2 #include<iostream>
      3 #include<algorithm>
      4 #include<cstring>
      5 #include<cstdio>
      6 #include<cmath>
      7 #include<vector>
      8 using namespace std;
      9 //const int MXN=10000010;
     10 const int mxn=150010;
     11 const int MX=70000;
     12 int read(){
     13     int x=0,f=1;char ch=getchar();
     14     while(ch<'0' || ch>'9'){if(ch=='-')f=-1;ch=getchar();}
     15     while(ch>='0' && ch<='9'){x=x*10+ch-'0';ch=getchar();}
     16     return x*f;
     17 }
     18 struct SGT{
     19     int l,r;
     20     int smm;
     21 }sg[mxn<<6];
     22 int scnt=0;
     23 int st[mxn<<6],top=0;
     24 int rot[mxn];
     25 inline int newnode(){
     26     if(top){
     27         int tmp=st[top--];
     28         sg[tmp].l=sg[tmp].r=sg[tmp].smm=0;
     29         return tmp;
     30     }
     31     return ++scnt;
     32 }
     33 inline void pushup(int rt){
     34     sg[rt].smm=sg[sg[rt].l].smm+sg[sg[rt].r].smm;return;
     35 }
     36 void del(int &rt){//线段树回收结点 
     37     if(!rt)return;
     38     if(sg[rt].l)del(sg[rt].l);
     39     if(sg[rt].r)del(sg[rt].r);
     40     st[++top]=rt; rt=0;
     41     return;
     42 }
     43 void update(int v,int p,int l,int r,int &rt){
     44     if(!rt)rt=newnode();
     45     if(l==r){sg[rt].smm+=v;return;}
     46     int mid=(l+r)>>1;
     47     if(p<=mid)update(v,p,l,mid,sg[rt].l);
     48     else update(v,p,mid+1,r,sg[rt].r);
     49     pushup(rt);
     50     if(!sg[rt].smm)del(rt);
     51     return;
     52 }
     53 //
     54 struct node{
     55     int l,r;
     56     int val,sz;
     57 }t[mxn];
     58 int n,Q;
     59 int cnt=0,ROT;
     60 int flag=0,fafa=0;//重构标记 
     61 int w[mxn];
     62 int bnode[mxn],bct=0;
     63 int dfn[mxn],dtime=0;
     64 void Build(int l,int r,int &rt){
     65     if(l>r){rt=0;return;}
     66     if(l==r){
     67         rt=dfn[l];
     68         update(1,t[rt].val,0,MX,rot[rt]);
     69         t[rt].sz=1;
     70         return;
     71     }
     72     int mid=(l+r)>>1;
     73     rt=dfn[mid];
     74     Build(l,mid-1,t[rt].l); Build(mid+1,r,t[rt].r);
     75     for(int i=l;i<=r;i++)//建线段树 
     76         update(1,t[dfn[i]].val,0,MX,rot[rt]);
     77     t[rt].sz=t[t[rt].l].sz+t[t[rt].r].sz+1;
     78     return;
     79 }
     80 void DEL(int &x){//删除替罪羊树结点 
     81     if(!x)return;
     82     del(rot[x]);//删除对应线段树
     83     DEL(t[x].l);
     84     bnode[++bct]=x;//中序遍历删除 
     85     DEL(t[x].r);
     86     t[x].sz=0;
     87     x=0;
     88     return; 
     89 }
     90 void Rebuild(int &x){
     91     DEL(x);
     92     for(int i=1;i<=bct;i++)dfn[i]=bnode[i];
     93     Build(1,bct,x);
     94     bct=0;//clear
     95     return;
     96 }
     97 void insert(int v,int k,int &rt){
     98     if(!rt){
     99         rt=++n;
    100         t[rt].val=v;
    101         t[rt].sz=1;
    102         update(1,t[rt].val,0,MX,rot[rt]);
    103         return;
    104     }
    105     update(1,v,0,MX,rot[rt]);
    106 /*  int L=sg[rot[t[rt].l]].smm;
    107     if(L>=k)insert(v,k,t[rt].l);
    108     else insert(v,k-L-1,t[rt].r);*/
    109     if(t[t[rt].l].sz<k)insert(v,k-t[t[rt].l].sz-1,t[rt].r);
    110         else insert(v,k,t[rt].l);
    111     t[rt].sz=t[t[rt].l].sz+t[t[rt].r].sz+1;
    112 //  int R=sg[rot[rt]].smm;
    113 //  if(L/(double)R>0.75 || L/(double)R<0.2)flag=rt;
    114     if(t[t[rt].l].sz/(double)t[rt].sz>0.72 || t[t[rt].l].sz/(double)t[rt].sz<0.25)flag=rt;
    115     if(flag==t[rt].l || flag==t[rt].r)fafa=rt;
    116     return;
    117 }
    118 int change(int v,int k,int rt){
    119 //  printf("change:%d %d %d
    ",v,k,rt);
    120     update(1,v,0,MX,rot[rt]);
    121     int tmp=0;//查找旧值 
    122 /*  int L=sg[rot[t[rt].l]].smm;
    123     if(L+1==k){tmp=t[rt].val;t[rt].val=v;}
    124     else if(L>=k)tmp=change(v,k,t[rt].l);
    125     else tmp=change(v,k-L-1,t[rt].r);*/
    126     if(t[t[rt].l].sz+1==k){
    127         tmp=t[rt].val;t[rt].val=v;
    128     }
    129     else if(t[t[rt].l].sz>=k)tmp=change(v,k,t[rt].l);
    130         else tmp=change(v,k-t[t[rt].l].sz-1,t[rt].r);
    131     update(-1,tmp,0,MX,rot[rt]);//删除旧值 
    132     return tmp;
    133 }
    134 vector<int>qt,qp;//区间 单点 
    135 void query(int L,int R,int x){//在平衡树上收集区间 
    136 //  if(!x)return;
    137     int ls=t[x].l,rs=t[x].r;
    138     int l=sg[rot[ls]].smm,r=sg[rot[x]].smm;
    139     //当前结点包含区间 
    140     if(L==1 && R==r){qt.push_back(rot[x]);return;}
    141     if(L<=l+1 && l+1<=R) qp.push_back(t[x].val);
    142     if(R<=l)query(L,R,t[x].l);
    143     else if(L>l+1)query(L-(l+1),R-(l+1),t[x].r);
    144     else{
    145         if(L<=l)query(L,l,t[x].l);
    146         if(r>l+1)query(1,R-l-1,t[x].r);
    147     }
    148     return;
    149 }
    150 int Query(int L,int R,int K){
    151     query(L,R,ROT);
    152     K--;
    153     int l=0,r=MX,s1=qt.size(),s2=qp.size();
    154     while(l<r){//二分答案 
    155         int mid=(l+r)>>1,smm=0;
    156         for(int i=0;i<s1;i++)smm+=sg[sg[qt[i]].l].smm;//区间 
    157         for(int i=0;i<s2;i++)//单点 
    158             if(l<=qp[i] && qp[i]<=mid)smm++;
    159         if(K<smm){
    160             for(int i=0;i<s1;i++)qt[i]=sg[qt[i]].l;
    161             r=mid;
    162         }
    163         else{
    164             for(int i=0;i<s1;i++)qt[i]=sg[qt[i]].r;
    165             l=mid+1;K-=smm;
    166         }
    167     }
    168     qt.clear();qp.clear();
    169     return l;
    170 }
    171 int lastans=0;
    172 int main(){
    173 //  freopen("in.txt","r",stdin);
    174     int i,j;
    175     n=read();
    176     for(i=1;i<=n;i++)w[i]=read();
    177     for(i=1;i<=n;i++)dfn[i]=i,t[i].val=w[i];
    178     Build(1,n,ROT);
    179     Q=read();
    180     char op[2];int x,y,k;
    181     while(Q--){
    182         scanf("%s",op);
    183         switch(op[0]){
    184             case 'Q':{
    185                 x=read()^lastans;y=read()^lastans;k=read()^lastans;
    186 //              x=read();y=read();k=read();
    187                 lastans=Query(x,y,k);
    188                 printf("%d
    ",lastans);
    189                 break;
    190             }
    191             case 'M':{
    192                 x=read()^lastans;k=read()^lastans;
    193 //              x=read();k=read();
    194                 change(k,x,ROT);
    195                 break;
    196             }
    197             case 'I':{
    198                 x=read()^lastans;k=read()^lastans;
    199 //              x=read();k=read();
    200                 flag=0;fafa=0;
    201                 insert(k,x-1,ROT);
    202                 if(flag){
    203                     if(flag==ROT)Rebuild(ROT);
    204                     else if(t[fafa].l==flag)Rebuild(t[fafa].l);
    205                         else if(t[fafa].r==flag)Rebuild(t[fafa].r);
    206                 }
    207                 break;
    208             }
    209         }
    210     }
    211     return 0;
    212 }
  • 相关阅读:
    二叉树知识拓展
    【2014年鄞州区】小幸福(e.pas/c/cpp)
    【2017年常州市】小X与队列 (queue)
    【2018年南海区甲组】扑克游戏(poker)
    【2018年南海区甲组】拆除桥墩(remove)
    【NOIP普及组模拟赛3】投影(skyline)
    常用正则表达式
    asp.net访问母版页控件方法
    C#文件读写操作
    C#读取csv格式文件
  • 原文地址:https://www.cnblogs.com/SilverNebula/p/6777290.html
Copyright © 2020-2023  润新知