HDU7072:Boring data structure problem——题解

https://acm.hdu.edu.cn/showproblem.php?pid=7072

自己翻译吧(

做法十分独特(

比赛时满脑子线段树结果真搞出了个线段树还能过的那种(

首先先用双端队列(删除不管)把插入全做了,同时把查询中间数转化成距离最左边元素多少个数。然后把双端队列里的数倒出来建立线段树维护区间内还活着多少个数,每次删除复杂度O(log),查询就变成了询问当前活着的数中第某个数是多少,复杂度也是O(log)。由于后两者操作个数不多,因此能够通过。

(后面放正解,先放这个做法的代码)

#include<cmath>
#include<queue>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=1e7+5;
const int M=3e6+5;
struct qry{
    int op,x,y;
}q[M];
deque<int>d;
int n,b[N],c[N],tr[N<<2];
void build(int a,int l,int r){
    if(l==r){
        tr[a]=1;return;
    }
    int mid=(l+r)>>1;
    build(a<<1,l,mid);build(a<<1|1,mid+1,r);
    tr[a]=tr[a<<1]+tr[a<<1|1];
}
void del(int a,int l,int r,int x){
    if(l==r){
        tr[a]=0;return; 
    }
    int mid=(l+r)>>1;
    if(x<=mid)del(a<<1,l,mid,x);
    else del(a<<1|1,mid+1,r,x);
    tr[a]=tr[a<<1]+tr[a<<1|1];
}
int qs(int a,int l,int r,int l1,int r1){
    if(r<l1||r1<l)return 0;
    if(l1<=l&&r<=r1)return tr[a];
    int mid=(l+r)>>1;
    return qs(a<<1,l,mid,l1,r1)+qs(a<<1|1,mid+1,r,l1,r1);
}
int qt(int a,int l,int r,int x){
    if(l==r)return b[l];
    int mid=(l+r)>>1;
    if(tr[a<<1]>=x)return qt(a<<1,l,mid,x);
    else return qt(a<<1|1,mid+1,r,x-tr[a<<1]);
}
int query(int x,int y){
    int sum=qs(1,1,n,1,y-1);
    return qt(1,1,n,sum+x);
}
int main(){
    int T,m=0,cnt=0;
    scanf("%d",&T);
    while(T--){
        char ch=0;
        while(ch<'A'||ch>'Z')ch=getchar();
        if(ch=='L'){
            ++m;d.push_front(++n);
        }
        if(ch=='R'){
            ++m;d.push_back(++n);
        }
        if(ch=='Q'){
            q[++cnt].op=1;q[cnt].x=ceil((double)(m+1)/2);q[cnt].y=d.front();
        }
        if(ch=='G'){
            m--;
            int x;scanf("%d",&x);
            q[++cnt].op=2;q[cnt].x=x;
        }
    }
    n=0;
    while(!d.empty()){
        b[++n]=d.front();
        d.pop_front();c[b[n]]=n;
    }
    build(1,1,n);
    for(int i=1;i<=cnt;i++){
        if(q[i].op==1){
            printf("%d
",query(q[i].x,c[q[i].y]));
        }
        if(q[i].op==2){
            del(1,1,n,c[q[i].x]);
        }
    }
    return 0;
}

正解还蛮简单的(当时有往那方面想,但是卡在不知道怎么查询上QAQ)

维护两个链表$L$和$R$(这俩拼起来可以双端队列,这么说应该就懂了),每次要查询的时候倒一下使得两边链表大小差不多相等就好,均摊就为$O(1)$的了。删除打懒标记即可。最终复杂度全是$O(1)$的真爽。

(跑的时间貌似和线段树差不多()

#include<list>
#include<cmath>
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=1e7+5;
list<int>L,R;
int sL,sR,s[N];
bool getdel(int x){return s[x]&0b01;}
bool getwhich(int x){return s[x]&0b10;}
int main(){
    int q,cnt=0;
    scanf("%d",&q);
    while(q--){
        char ch=0;
        while(ch<'A'||ch>'Z')ch=getchar();
        if(ch=='L'){
            ++sL;L.push_back(++cnt);
            s[cnt]=0b00;
        }
        if(ch=='R'){
            ++sR;R.push_back(++cnt);
            s[cnt]=0b10;
        }
        if(ch=='Q'){
            while(sL>sR){
                int x=L.front();L.pop_front();
                if(getdel(x))continue;
                R.push_front(x);
                --sL;++sR;s[x]|=0b10;
            }
            while(sL<=sR){
                int x=R.front();R.pop_front();
                if(getdel(x))continue;
                L.push_front(x);
                ++sL;--sR;s[x]&=0b01;
            }
            printf("%d
",L.front());
        }
        if(ch=='G'){
            int x;scanf("%d",&x);
            s[x]|=0b01;
            if(!getwhich(x))--sL;
            else --sR;
        }
    }
    return 0;
}

+++++++++++++++++++++++++++++++++++++++++++

 +本文作者:luyouqi233。               +

 +欢迎访问我的博客:http://www.cnblogs.com/luyouqi233/+

+++++++++++++++++++++++++++++++++++++++++++

原文地址:https://www.cnblogs.com/luyouqi233/p/15153823.html