[bzoj3065]带插入区间K小值 解题报告

来源:互联网 发布:浙江严查环保数据作假 编辑:程序博客网 时间:2024/05/03 08:46

傻逼怎么做:
先用一个块链维护序列,做到O(n)插入,O(1)比较两个点在序列中位置的大小。时间复杂度是O(nn)
再用一个块链维护权值,每个块维护按位置排序的序列。查询的时候从小到大枚举每个块,先在块里二分统计块内在查询区间中的个数,然后如果发现答案在这个块里,就直接暴力找到第k小的是哪个。取块大小等于O(nlog2n),则时间复杂度是O(qnlog2n)
总的时间复杂度就是O(nn+qnlog2n),然后不知道是我姿势不对还是什么情况。。写了9K。
鸟哥的做法:
用块链维护,每个块维护每种权值出现次数的前缀和、和对一块权值出现次数的前缀和。这样时间复杂度就是O((n+q)n),而且比我的做法好写很多。。
我的代码:

#include<cstdio>#include<iostream>using namespace std;#include<algorithm>#include<cassert>#include<cstring>#include<cmath>const int N=7e4+5,A=7e4+5,Q=175000+5;int n;char * cp=(char *)malloc(30000000);void in(int &x){    bool flag=0;    while(*cp<'0'||*cp>'9')flag=*cp++=='-';    for(x=0;*cp>='0'&&*cp<='9';)x=x*10+(*cp++^'0');    if(flag)x=-x;}char * os=(char *)malloc(500000),* op=os;void out(int x){    if(x){        out(x/10);        *op++='0'+x%10;    }}int w[N];int p_pos1[N],p_pos2[N];int w_pos1[N],w_pos2[N];//只有插入 const int S=200;//const int S=200;//S∈[200,300]//N/S∈[233,350]int ptot=1;int p_block[1000+5][1000+5];int p_num[1000+5],p_pos0[1000+5];void p_out(){    printf("-----out p:\n");    for(int i=1;i<ptot;++i){        printf("%d:",p_num[i]);        for(int j=1;j<=p_block[p_num[i]][0];++j)printf("%d(%d,%d) ",p_block[p_num[i]][j],p_pos1[p_block[p_num[i]][j]],p_pos2[p_block[p_num[i]][j]]);        puts("");    }}void p_build(){    for(int i=1;i<=n;++i){        if(p_block[ptot][0]==S)++ptot;        ++p_block[ptot][0];        p_block[ptot][p_block[ptot][0]]=i;        p_pos1[i]=ptot,p_pos2[i]=p_block[ptot][0];    }    for(int i=ptot++;i;--i)p_num[i]=p_pos0[i]=i;}void p_insert(int x){    int i=1,j;    for(;x-p_block[p_num[i]][0]>0&&i+1<ptot;++i)x-=p_block[p_num[i]][0];    int bi=p_num[i];    for(j=++p_block[bi][0];j>x;--j){        p_block[bi][j]=p_block[bi][j-1];        p_pos2[p_block[bi][j]]=j;    }    p_block[bi][x]=n;    p_pos1[n]=bi,p_pos2[n]=x;    if(p_block[bi][0]==S<<1){        p_block[bi][0]=S;        memcpy(p_block[ptot]+1,p_block[bi]+S+1,sizeof(int)*S);        p_block[ptot][0]=S;        for(j=S;j;--j){            p_pos1[p_block[ptot][j]]=ptot;            p_pos2[p_block[ptot][j]]=j;        }        x=i;        for(i=ptot-1;i>x;--i){            p_num[i+1]=p_num[i];            p_pos0[p_num[i]]=i+1;        }        p_num[x+1]=ptot;        p_pos0[ptot]=x+1;        ++ptot;    }}bool p_cmp(const int &a,const int &b){    return p_pos1[a]!=p_pos1[b]?p_pos0[p_pos1[a]]<p_pos0[p_pos1[b]]:p_pos2[a]<p_pos2[b];}//需要支持插入/删除 const int B=500;//const int B=1050;//B∈[500,2000]//N/B∈[35,140]struct WS{    int num,w;    bool operator < (const WS & o)const{        return w<o.w;    }}w_tmp[35000+5];int w_q[2000+5],w_h,w_t;int w_block[2000+5][2000+5],w_sorted[2000+5][2000+5];int w_last[2000+5],w_next[2000+5],w_first;void w_out(){    printf("---out w\n");    for(int i=w_first;i;i=w_next[i]){        printf("w_block[%d]\n",i);        printf("sorted by w:");        for(int j=1;j<=w_block[i][0];++j)printf("%d ",w_block[i][j]);        puts("");        printf("sorted by p:");        for(int j=1;j<=w_block[i][0];++j)printf("%d ",w_sorted[i][j]);        puts("");    }    //for(int i=1;i<n;++i)printf("pos[%d]={%d,%d}\n",i,w_pos1[i],w_pos2[i]);}void w_build(){    for(int i=1;i<=400;++i)w_q[i-1]=i;    for(int i=1;i<=n;++i)w_tmp[i]=(WS){i,w[i]};    sort(w_tmp+1,w_tmp+n+1);    for(int i=1;i<=n;++i){        if(w_block[w_q[w_h]][0]==B)++w_h;        w_block[w_q[w_h]][++w_block[w_q[w_h]][0]]=w_tmp[i].num;        w_pos1[w_tmp[i].num]=w_q[w_h],w_pos2[w_tmp[i].num]=w_block[w_q[w_h]][0];    }    w_first=1;    for(int i=++w_h;i>1;--i){        w_last[i]=i-1;        w_next[i-1]=i;    }    for(int i=w_h;i;--i){        memcpy(w_sorted[i]+1,w_block[i]+1,sizeof(int)*w_block[i][0]);        sort(w_sorted[i]+1,w_sorted[i]+w_block[i][0]+1,p_cmp);    }}void w_split(int i){    if(w_block[i][0]>=B<<1){        int j;        w_block[w_q[w_h]][0]=w_block[i][0]-(w_block[i][0]>>1);        memcpy(w_block[w_q[w_h]]+1,w_block[i]+1+(w_block[i][0]>>1),sizeof(int)*w_block[w_q[w_h]][0]);        for(j=w_block[w_q[w_h]][0];j;--j){            w_pos1[w_block[w_q[w_h]][j]]=w_q[w_h];            w_pos2[w_block[w_q[w_h]][j]]=j;        }        w_sorted[w_q[w_h]][0]=w_sorted[i][0]=0;        int tmp;        for(j=1;j<=w_block[i][0];++j){            tmp=w_pos1[w_sorted[i][j]];            w_sorted[tmp][++w_sorted[tmp][0]]=w_sorted[i][j];        }        w_block[i][0]>>=1;        w_next[w_q[w_h]]=w_next[i];        w_last[w_q[w_h]]=i;        w_last[w_next[i]]=w_q[w_h];        w_next[i]=w_q[w_h];        w_h=(w_h+1)%400;    }}int tmp[2000+5];void w_delete(int x){    int i=w_pos1[x],j;    for(j=w_pos2[x];j<w_block[i][0];++j){        w_block[i][j]=w_block[i][j+1];        w_pos2[w_block[i][j]]=j;    }    j=w_block[i][0];    while(w_sorted[i][j]!=x)--j;    for(;j<w_block[i][0];++j)w_sorted[i][j]=w_sorted[i][j+1];    --w_block[i][0];    if(w_block[i][0]){        if(w_block[i][0]<B&&w_next[i]){            memcpy(w_block[i]+w_block[i][0]+1,w_block[w_next[i]]+1,sizeof(int)*w_block[w_next[i]][0]);            memcpy(w_block[w_next[i]]+1,w_block[i]+1,sizeof(int)*(w_block[i][0]+w_block[w_next[i]][0]));            for(j=w_block[i][0];j;--j)w_pos1[w_block[i][j]]=w_next[i];            for(j=w_block[w_next[i]][0]+w_block[i][0];j>w_block[i][0];--j)w_pos2[w_block[w_next[i]][j]]=j;            int k=1,o=1;            for(j=1;j<=w_block[i][0]&&k<=w_block[w_next[i]][0];)                if(p_cmp(w_sorted[i][j],w_sorted[w_next[i]][k])){                    tmp[o++]=w_sorted[i][j++];                }                else{                    tmp[o++]=w_sorted[w_next[i]][k++];                }            memcpy(tmp+o,w_sorted[i]+j,sizeof(int)*(w_block[i][0]-j+1));            memcpy(tmp+o,w_sorted[w_next[i]]+k,sizeof(int)*(w_block[w_next[i]][0]-k+1));            w_block[w_next[i]][0]+=w_block[i][0];            memcpy(w_sorted[w_next[i]]+1,tmp+1,sizeof(int)*w_block[w_next[i]][0]);            w_next[w_last[i]]=w_next[i];            w_last[w_next[i]]=w_last[i];            if(i==w_first)w_first=w_next[i];            w_q[w_t]=i;            w_t=(w_t+1)%400;            w_split(w_next[i]);        }    }    else{        w_next[w_last[i]]=w_next[i];        w_last[w_next[i]]=w_last[i];        if(i==w_first)w_first=w_next[i];        w_q[w_t]=i;        w_t=(w_t+1)%400;    }}void w_insert(int x){    int i=w_first,j;    if(i==0){        i=w_first=w_q[w_h];        w_h=(w_h+1)%400;        w_last[i]=w_next[i]=0;        w_block[i][0]=1,w_block[i][1]=x;        w_sorted[i][1]=x;        w_pos1[x]=i,w_pos2[x]=1;    }    else{        while(w_next[i]&&w[w_block[w_next[i]][1]]<w[x])i=w_next[i];        for(j=++w_block[i][0];j>1&&w[w_block[i][j-1]]>=w[x];--j){            w_block[i][j]=w_block[i][j-1];            w_pos2[w_block[i][j]]=j;        }        w_block[i][j]=x;        w_pos1[x]=i,w_pos2[x]=j;        for(j=w_block[i][0];j>1&&p_cmp(x,w_sorted[i][j-1]);--j)w_sorted[i][j]=w_sorted[i][j-1];        w_sorted[i][j]=x;        w_split(i);    }}int main(){    freopen("bzoj_3065.in","r",stdin);    freopen("bzoj_3065.out","w",stdout);    fread(cp,1,30000000,stdin);    int m;    in(n);    for(int i=1;i<=n;++i)in(w[i]);    p_build();    w_build();    ++n;    //p_out(),w_out();    int q;    in(q);    int x,y,k,val;    int i,j;    int lastans=0;    int cnt;    while(q--){        while(*cp<'A'||*cp>'Z')++cp;        //printf("-------------\n");        switch(*cp++){            case 'Q':                in(x),in(y),in(k);                //x^=lastans,y^=lastans,k^=lastans;                for(i=1;x>p_block[p_num[i]][0];++i){                    x-=p_block[p_num[i]][0];                    y-=p_block[p_num[i]][0];                }                x=p_block[p_num[i]][x];                for(;y>p_block[p_num[i]][0];++i)y-=p_block[p_num[i]][0];                y=p_block[p_num[i]][y];                //printf("Get range:[%d,%d]\n",x,y);                for(i=w_first;;i=w_next[i]){                    cnt=upper_bound(w_sorted[i]+1,w_sorted[i]+w_block[i][0]+1,y,p_cmp)-lower_bound(w_sorted[i]+1,w_sorted[i]+w_block[i][0]+1,x,p_cmp);                    //printf("cnt(%d)=%d-%d\n",i,upper_bound(w_sorted[i]+1,w_sorted[i]+w_block[i][0]+1,y,p_cmp)-w_sorted[i],lower_bound(w_sorted[i]+1,w_sorted[i]+w_block[i][0]+1,x,p_cmp)-w_sorted[i]);                    if(k>cnt)k-=cnt;                    else{                        for(j=1;j<=w_block[i][0];++j)                            if(!p_cmp(w_block[i][j],x)&&!p_cmp(y,w_block[i][j])&&--k==0){                                lastans=w[w_block[i][j]];                                break;                            }                        break;                    }                }                if(lastans)out(lastans);                else *op++='0';                *op++='\n';                //printf("%d\n",lastans);                break;            case 'M':                in(x),in(val);                //x^=lastans,val^=lastans;                for(i=1;x>p_block[p_num[i]][0];++i)x-=p_block[p_num[i]][0];                x=p_block[p_num[i]][x];                w_delete(x);                w[x]=val;                w_insert(x);                break;            case 'I':                in(x),in(val);                //x^=lastans,val^=lastans;                w[n]=val;                p_insert(x);                w_insert(n);                ++n;                break;        }        //p_out(),w_out();    }    fwrite(os,1,op-os,stdout);}

总结:
①一定要生成极限数据测试!
②注意发掘题目的性质——既然权值是静态的,而位置是动态的,那么我们为什么要用块链维护权值呢?
③块链其实可以在每个块维护O(n)的信息,因为它最多只会产生O(n)个块。如果要删除的话也没关系,我们在删除的时候其实并不需要把比较小的块合并,因为即使不那样也只会有O(n)个块。

0 0