1. 程式人生 > >【NOIP2017】【UOJ334】【LOJ2319】列隊

【NOIP2017】【UOJ334】【LOJ2319】列隊

【題目連結】

【前置技能】

  • 動態開節點線段樹

【題解】

  • 本題好像有什麼樹狀陣列就能做的解法,但我只會線段樹和Splay的大力亂搞,畢竟思考起來比較直觀一點。以下是線段樹做法,程式碼比較醜,懶得改了。
  • 仔細觀察發現一次變動影響的點不是很多,那我們就建立支援單點修改的線段樹。對每一行的前m1個點維護兩棵線段樹,對最後一列維護兩棵線段樹。其中第一棵線段樹表示的是原來的人,第二棵線段樹表示的是修改後進來的人。一開始兩棵線段樹都是空的,表示每個位置上都是原來的人。
  • 在某一個人(x,y)移動的時候,先在第一棵線段樹上看一下未移動的人是否大於等於y個,若滿足,則在第一棵線段樹上二分找到第
    y
    個位置並打上刪除標記;否則在第二棵線段樹上二分找到答案並打上刪除標記。然後用同樣的方法找到維護列的線段樹上第x個數,把它打上刪除標記,並把這個數加到這一行的尾部。最後要把該詢問的答案加到最後一列的尾部。
  • 注意移動的人就在最後一列的情況要特判一下,直接在維護列的線段樹上修改即可。
  • 因為每次最多修改O(logN)個節點,動態開節點,空間複雜度O(QlogN)。時間複雜度O(QlogN)

【程式碼】

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define LL  long long
#define MAXN 300100 using namespace std; LL n, m, Q; template <typename T> void chkmin(T &x, T y){x = min(x, y);} template <typename T> void chkmax(T &x, T y){x = max(x, y);} template <typename T> void read(T &x){ x = 0; int f = 1; char ch = getchar(); while (!isdigit(ch)) {if
(ch == '-') f = -1; ch = getchar();} while (isdigit(ch)) {x = x * 10 + ch - '0'; ch = getchar();} x *= f; } struct Segment_TreeI{ struct info{int ls, rs, sum;}a[MAXN * 100]; int root[MAXN], cnt, n; void push_up(int pos){ a[pos].sum = a[a[pos].ls].sum + a[a[pos].rs].sum; } void init(int x){ n = x, cnt = 0; } void modify(int &pos, int l, int r, int p, int d){ if (!pos) pos = ++cnt; if (l == r) { a[pos].sum += d; return; } int mid = (l + r) >> 1; if (p <= mid) modify(a[pos].ls, l, mid, p, d); else modify(a[pos].rs, mid + 1, r, p, d); push_up(pos); } void modify(int T, int p, int d){ modify(root[T], 1, n, p, d); } LL query(int pos, int l, int r, int k){ if (l == r) return l; int mid = (l + r) >> 1; int tmp = (mid - l + 1) - a[a[pos].ls].sum; if (tmp >= k) return query(a[pos].ls, l, mid, k); else return query(a[pos].rs, mid + 1, r, k - tmp); } LL query(int T, int k){ return query(root[T], 1, n, k); } int size(int T){ return a[root[T]].sum; } }sgtI; struct Segment_TreeII{ struct info{int ls, rs, sum; LL data;}a[MAXN * 100]; int root[MAXN], cnt, n, used[MAXN]; void push_up(int pos){ a[pos].sum = a[a[pos].ls].sum + a[a[pos].rs].sum; } void init(int x){ n = x, cnt = 0; } void modify(int &pos, int l, int r, int p, LL d){ if (!pos) pos = ++cnt; if (l == r) { a[pos].data = d; if (d) a[pos].sum = 1; else a[pos].sum = 0; return; } int mid = (l + r) >> 1; if (p <= mid) modify(a[pos].ls, l, mid, p, d); else modify(a[pos].rs, mid + 1, r, p, d); push_up(pos); } void modify(int T, int p, LL d){ modify(root[T], 1, n, p, d); } LL query(int pos, int l, int r, int k){ if (l == r) return a[pos].data; int mid = (l + r) >> 1; int tmp = a[a[pos].ls].sum; if (tmp >= k) return query(a[pos].ls, l, mid, k); else return query(a[pos].rs, mid + 1, r, k - tmp); } LL query(int T, int k){ return query(root[T], 1, n, k); } int get(int pos, int l, int r, int k){ if (l == r) return l; int mid = (l + r) >> 1; int tmp = a[a[pos].ls].sum; if (tmp >= k) return get(a[pos].ls, l, mid, k); else return get(a[pos].rs, mid + 1, r, k - tmp); } int get(int T, int k){ return get(root[T], 1, n, k); } }sgtII; int main(){ read(n), read(m), read(Q); sgtI.init(max(m, n)); sgtII.init(Q); while (Q--){ int x, y; read(x), read(y); if (y == m){ int tmp = n - sgtI.size(n + 1); LL id; if (tmp >= x) id = 1ll * sgtI.query(n + 1, x) * m; else id = sgtII.query(n + 1, x - tmp); printf("%lld\n", id); if (tmp >= x) sgtI.modify(n + 1, id / m, 1); else sgtII.modify(n + 1, sgtII.get(n + 1, x - tmp), 0); sgtII.modify(n + 1, ++sgtII.used[n + 1], id); } else { int tmp = m - sgtI.size(x) - 1; LL id, ID; if (tmp >= y) id = 1ll * sgtI.query(x, y) + 1ll * (x - 1) * m; else id = sgtII.query(x, y - tmp); ID = id; printf("%lld\n", ID); if (tmp >= y) sgtI.modify(x, id - 1ll * (x - 1) * m, 1); else sgtII.modify(x, sgtII.get(x, y - tmp), 0); tmp = n - sgtI.size(n + 1); if (tmp >= x) id = 1ll * sgtI.query(n + 1, x) * m; else id = sgtII.query(n + 1, x - tmp); sgtII.modify(x, ++sgtII.used[x], id); if (tmp >= x) sgtI.modify(n + 1, id / m, 1); else sgtII.modify(n + 1, sgtII.get(n + 1, x - tmp), 0); sgtII.modify(n + 1, ++sgtII.used[n + 1], ID); } } return 0; }