[NOIP2017]列队 (Splay)

题目链接

NOIP2017真的是不按常理出牌:
1、数学题不在Day2T1
2、一道水题一道细节极多的模拟题一道不知道怎么形容的题(小凯的疑惑)(因为我太菜了)
3、3道大火题

当时看到列队这题是毫无头绪的,因为数据大得让你存都存不下,于是果断打了个30分暴力(如果打个离散化还能多骗20分)。
蓦然回首,豁然开朗。

思考可知,一个人出列影响的只是当前一行和最后一列,于是,我们只需要对每一行和最后一列分别用数据结构维护,这个数据结构要支持
1、查询并删除第\(k\)个数。
2、在末尾插入一个数。
若能维护这些,设每次操作的位置为\((x,y)\),那我们只需要把第\(x\)行第\(y\)个数删除并插入到最后一列的末尾去,然后把最后一列第\(x\)个数删除并插入到第\(x\)行的末尾就行了。

接下来考虑用什么数据结构。

\(Splay\)显然是支持这些操作的(事实上几乎所有平衡树都支持,然而我只会常数巨大的\(Splay\)
然后面临的一个问题就是\(n*m\)的范围达到了\((30W)^2\),显然直接开是开不下的。
容易发现\(q\)次操作后,绝大部分人的相对位置是不变的,所以我们可以用点表示区间,查询&删除\(k\)时把这个区间\([l,r]\)分成三部分,
·1、\([l,k-1]\)
·2、\(k\)
·3、\([k+1,r]\)
第一个区间就是原来的点,第三个区间是新加的点,第二个就是我们需要的,直接丢掉,把第一个和第三个点连起来(具体就是把第三部分插入到第一部分和第一部分的右儿子之间)。特别地,如果\(k\)刚好等于这个点维护的区间的两个端点之一,直接把端点维护的区间减一减掉\(k\)就行了。

\(100\)\(line\)\(code\)\(debug\)了好久唔唔唔。

#include 
#include 
#define ll long long
#define getchar() (S==T&&(T=(S=BB)+fread(BB,1,1<<15,stdin),S==T)?EOF:*S++)
char BB[1<<15],*S=BB,*T=BB;
inline int read(){
    int s = 0, w = 1;
    char ch = getchar();
    while(ch < '0' || ch > '9'){ if(ch == '-') w = -1; ch = getchar();}
    while(ch >= '0' && ch <= '9') s = s * 10 + ch - '0', ch = getchar();
    return s * w;
}
const int MAXN = 300010;
const int MAX = 10000010;
int n, m, q;
int cnt, size[MAX], son[MAX][2], fa[MAX];
ll L[MAX], R[MAX];
struct Splay_Tree{
    int root;
    inline void pushup(int x){ size[x] = size[son[x][0]] + size[son[x][1]] + R[x] - L[x] + 1; }
    inline int Newnode(ll l, ll r){
        L[++cnt] = l; R[cnt] = r;size[cnt] = r - l + 1;
        return cnt;
    }
    inline void init(ll l, ll r){ root = Newnode(l, r); }
    inline void rotate(int x){
        int y = fa[x]; int z = fa[y]; int k = (son[y][1] == x);
        son[z][son[z][1] == y] = x; fa[x] = z;
        son[y][k] = son[x][k ^ 1];  fa[son[x][k ^ 1]] = y;
        son[x][k ^ 1] = y; fa[y] = x;
        pushup(y); pushup(x);
    }
    inline void Splay(int x, int goal){
        if(x == goal) return;
        int y, z;
        while(fa[x] != goal){
          y = fa[x]; z = fa[y];
          if(z != goal) (son[z][1] == y) ^ (son[y][1] == x) ? rotate(x) : rotate(y);
          rotate(x);
        }
        if(!goal) root = x;
    }
    inline void insert(ll x){
        int now = root;
        while(son[now][1]) now = son[now][1];
        int t = Newnode(x, x);
        son[now][1] = t; fa[t] = now; Splay(t, 0);
    }
    inline ll split(int x, ll k){    //分裂点x,删除k
        ll s = L[x], t = R[x];
        if(k != s && k != t){
          int b = Newnode(k + 1, t); R[x] = k - 1;
          son[b][1] = son[x][1]; fa[son[x][1]] = b;
          son[x][1] = b; fa[b] = x; 
          Splay(b, 0);
        }
        else{
          if(k == t) --R[x];
          else ++L[x];
          --size[x];
          Splay(x, 0);
        }
        return k;
    }
    inline ll popkth(int k){    //删除并返回第k大
        int x = root;
        while(233){
          if(size[son[x][0]] >= k) x = son[x][0];
          else {
            k -= size[son[x][0]];
            if(k <= R[x] - L[x] + 1)
              return split(x, k + L[x] - 1);
            k -= R[x] - L[x] + 1; x = son[x][1];
          }
        }
    }
    inline int build(int l, int r, int f){  //对最后一列的建树
        if(l > r) return 0;
        int mid = (l + r) >> 1;
        int now = Newnode((ll)mid * m, (ll)mid * m);
        fa[now] = f;
        son[now][0] = build(l, mid - 1, now);
        son[now][1] = build(mid + 1, r, now);
        pushup(now); 
        return now;
    }
}splay[MAXN];
int a, b;
ll x, y;
int main(){
    n = read(); m = read(); q = read();
    for(int i = 1; i <= n; ++i)    //每行一棵Splay维护前m-1个数
       splay[i].init((ll)(i - 1) * m + 1, (ll)i * m - 1);   //点表示区间
    splay[0].root = splay[0].build(1, n, 0);
    for(int i = 1; i <= q; ++i){
       a = read(); b = read();
       if(b == m){
         printf("%lld\n", x = splay[0].popkth(a));
         splay[0].insert(x);
         continue;
       }
       printf("%lld\n", x = splay[a].popkth(b));
       splay[a].insert(splay[0].popkth(a));
       splay[0].insert(x);
    }
    return 0;
} 

转载于:https://www.cnblogs.com/Qihoo360/p/9628656.html

你可能感兴趣的:([NOIP2017]列队 (Splay))