zoukankan      html  css  js  c++  java
  • NOIP2017 Day2 T3 列队(treap)

      可以直接用treap上大模拟...n+1个treap维护n行的前m-1个点和最后一列。

      需要支持删除一个点或者一段区间,而空间并不支持存下所有的点的时候,可以用一个点代替一个区间,记录区间首项的值和区间长度,这样每次查询某个点x的时候就可以用x在某个点y代表的区间里的rank来得到x的值,然后把x删去的时候,就把y这个区间从$[l,r]$拆分成$[l,x-1]$和$[x+1,r]$,重新加入。

      类似的题有NOI超级钢琴

    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<cstdio>
    #include<algorithm>
    #define ll long long
    #define int long long
    #define lt tree[x].ls
    #define rt tree[x].rs
    using namespace std;
    const int maxn=6000010;
    struct poi{ll beg; int rnd, size, ls, rs, len;}tree[maxn];
    int n, m, Q, x, y, tott, tmp, rk;
    ll ans;
    int root[maxn], cnt[maxn], Len[maxn];
    inline void read(int &k)
    {
        int f=1; k=0; char c=getchar();
        while(c<'0' || c>'9') c=='-' && (f=-1), c=getchar();
        while(c<='9' && c>='0') k=k*10+c-'0', c=getchar();
        k*=f;
    }
    inline void build(int &x, ll beg, int len)
    {
        tree[x=++tott].beg=beg; tree[x].size=1; 
        tree[x].len=Len[x]=len;
        tree[x].rnd=rand()<<15|rand();
    }
    inline void up(int x) 
    {
        tree[x].size=tree[lt].size+tree[rt].size+1; 
        tree[x].len=tree[lt].len+tree[rt].len+Len[x];
    }
    void split(int x, int &l, int &r, int k)
    {
        if(!k) l=0, r=x;
        else if(tree[x].size==k) l=x, r=0;
        else if(tree[lt].size>=k) r=x, split(lt, l, lt, k), up(x);
        else l=x, split(rt, rt, r, k-tree[lt].size-1), up(x);
    }
    void rank(int x, int k)
    {
        if(tree[lt].len<k && tree[lt].len+Len[x]>=k) ans=k-tree[lt].len, rk+=tree[lt].size+1;
        else if(k<=tree[lt].len) rank(lt, k);
        else rk+=tree[lt].size+1, rank(rt, k-tree[lt].len-Len[x]);
    }
    void merge(int &x, int l, int r)
    {
        if(!l || !r) x=l+r;
        else if(tree[l].rnd<tree[r].rnd) x=l, merge(rt, rt, r), up(x);
        else x=r, merge(lt, l, lt), up(x);
    }
    inline void disc(int &Root, ll &poi, int pos)
    {
        int x, y;
        rk=0; rank(Root, pos);
        split(Root, Root, y, rk); if(rk-1) split(Root, Root, x, rk-1); else x=Root;
        if(ans-1) build(tmp, tree[x].beg, ans-1), merge(Root, Root, tmp);
        if(Len[x]-ans) build(tmp, tree[x].beg+ans*(Root==root[n+1]?m:1), Len[x]-ans), merge(Root, Root, tmp);
        merge(Root, Root, y); poi=x; 
    }
    inline ll query(int posx, int posy)
    {
        if(posy!=m)
        {
            ll ANS, poi;
            disc(root[posx], poi, posy); ANS=tree[poi].beg+ans-1;
            build(tmp, ANS, 1); merge(root[n+1], root[n+1], tmp);
            disc(root[n+1], poi, posx); poi=tree[poi].beg+(ans-1)*m; 
            build(tmp, poi, 1); merge(root[posx], root[posx], tmp);
            return ANS;
        }
        ll poi;
        disc(root[n+1], poi, posx); poi=tree[poi].beg+(ans-1)*m;
        build(tmp, poi, 1); merge(root[n+1], root[n+1], tmp);
        return poi;
    }
    #undef int
    int main()
    {
        srand(19260817);
        read(n); read(m); read(Q);
        for(int i=1;i<=n;i++) build(root[i], 1ll*1+(i-1)*m, m-1); build(root[n+1], m, n); 
        for(int i=1;i<=Q;i++) read(x), read(y), printf("%lld
    ", query(x, y));
    }
    View Code

      线段树好写很多,而且非常快。

      可以发现如果不是从最后一列加进来的数的话一定是递增的,那么这些数我们可以直接用n棵权值线段树来维护找第k大即可,把删点改为加点,就不会爆空间,然后剩下从最后一列加进来的直接丢进一个vector里面,如果查询的数不在线段树里就直接在vector里查询。最后一列维护的方法同理。

    #include<iostream>
    #include<cstring>
    #include<cstdlib>
    #include<cstdio>
    #include<algorithm>
    #include<vector>
    #define ll long long
    #define lt tree[x].ls
    #define rt tree[x].rs
    using namespace std;
    const int maxn=1000010;
    struct poi{int ls, rs, size;}tree[maxn<<2];
    vector<ll>h[maxn];
    int n, m, q, tott, x, y;
    ll mx;
    int root[maxn];
    inline void read(int &k)
    {
        int f=1; k=0; char c=getchar();
        while(c<'0' || c>'9') c=='-' && (f=-1), c=getchar();
        while(c<='9' && c>='0') k=k*10+c-'0', c=getchar();
        k*=f;
    }
    void update(int &x, int l, int r, int cx)
    {
        if(!x) x=++tott; tree[x].size++;
        if(l==r) return;
        int mid=(l+r)>>1;
        if(cx<=mid) update(lt, l, mid, cx);
        else update(rt, mid+1, r, cx);
    }
    int query(int x, int l, int r, int k)
    {
        if(l==r) return l;
        int mid=(l+r)>>1, lsize=mid-l+1-tree[lt].size;
        if(lsize>=k) return query(lt, l, mid, k);
        return query(rt, mid+1, r, k-lsize);
    }
    inline ll delr(int x, ll y)
    {
        ll pos=query(root[n+1], 1, mx, x); update(root[n+1], 1, mx, pos);
        ll ans=pos<=n?1ll*pos*m:h[n+1][pos-n-1];
        h[n+1].push_back(y?y:ans);
        return ans;
    }
    inline ll dell(int x, ll y)
    {
        ll pos=query(root[x], 1, mx, y); update(root[x], 1, mx, pos);
        ll ans=pos<m?1ll*(x-1)*m+pos:h[x][pos-m];
        h[x].push_back(delr(x, ans));
        return ans;
    }
    int main()
    {
        read(n); read(m); read(q); mx=max(n, m)+q;
        for(int i=1;i<=q;i++)
        {
            read(x); read(y);
            if(y==m) printf("%lld
    ", delr(x, 0));
            else printf("%lld
    ", dell(x, y));
        }
    }
    View Code
  • 相关阅读:
    UICollectionViewController用法
    UISegmentedControl的详细使用
    &#x开头的是什么编码呢。浏览器可以解释它。如&#20013;&#22269;等同与中文"中国"?
    Random.nextint() 和Math.random()的区别
    UIGestureRecognizer ios手势识别温习
    [工具]Mac平台开发几个网络抓包工具(sniffer)
    IOS中Json解析的四种方法
    iOS官方Sample大全
    Ubuntu 16.04安装cuda7.5 GCC
    Ubuntu中升极下载4.2内核
  • 原文地址:https://www.cnblogs.com/Sakits/p/8075614.html
Copyright © 2011-2022 走看看