[NOIP2017 TG D2T3]列队
题目大意:有一个$n \times m$的方阵,第$i$行第$j$列的人的编号是$(i-1) \times m + j$。
现在有$q$个出列操作,每次让一个人出列,然后让这个人所在行向左看齐,再让最后一列向前看齐,最后让这个人站到第$n$行第$m$列的位置。
你需要输出每次出列的人的编号。
题解:可以每行维护一棵平衡树,再给最后一列维护一棵平衡树(虽然正解是用树状数组)。
发现每行的人初始编号是连续的,而对于$9 \times 10 ^ {10}$的人数,$3 \times 10 ^ {5}$次询问改变的人数其实并不多。
所以,我们把同一行内编号连续的一段缩成一个点,然后需要出列时再分裂即可。
卡点:因为正解是树状数组,所以我洛谷被玄学卡$RE$($UOJ$过的),数组大小调了好几次,然后发现开$O(2)$,数组开的比$UOJ$大一些就不$RE$了。。。
C++ Code:(洛谷)
// luogu-judger-enable-o2 #include <cstdio> #include <cstdlib> #define maxn 5500010 using namespace std; struct node{ int l, r; long long head; void operator -= (long long a) {l -= a; r -= a;} } val[maxn]; int n, m, q; int lc[maxn], rc[maxn], num[maxn], sz[maxn], tg[maxn], idx; struct treap { int root, len; int ta, tb, tmp, tmp6; int nw(node x) { val[++idx] = x; sz[idx] = 1; num[idx] = rand(); lc[idx] = rc[idx] = 0; return idx; } void update(int p) {sz[p] = sz[lc[p]] + sz[rc[p]] + 1;} void pushdown(int p) { tmp6 = tg[p]; tg[p] = 0; val[lc[p]] -= tmp6; val[rc[p]] -= tmp6; tg[lc[p]] += tmp6; tg[rc[p]] += tmp6; } void splitl(int rt, int k, int &x, int &y) { if (!rt) x = y = 0; else { if (val[rt].l <= k) splitl(rc[rt], k, rc[rt], y), x = rt; else splitl(lc[rt], k, x, lc[rt]), y = rt; update(rt); } } void splitr(int rt, int k, int &x, int &y) { if (!rt) x = y = 0; else { pushdown(rt); if (val[rt].r <= k) splitr(rc[rt], k, rc[rt], y), x = rt; else splitr(lc[rt], k, x, lc[rt]), y = rt; update(rt); } } int merge(int x, int y) { if (!x || !y) return x | y; if (num[x] > num[y]) { pushdown(x); rc[x] = merge(rc[x], y); update(x); return x; } else { pushdown(y); lc[y] = merge(x, lc[y]); update(y); return (y); } } void build(node p) { root = nw(p); } void insert(long long p, int k = -1){ if (k == -1) k = len; if (!root) root = nw((node) {k, k, p}); else root = merge(root, nw((node) {k, k, p})); } long long nxt(int k) { splitr(root, k - 1, ta, tmp); splitl(tmp, k, tmp, tb); int a = nw((node) {val[tmp].l, k - 1, val[tmp].head}), b = nw((node) {k + 1, val[tmp].r, val[tmp].head + k + 1 - val[tmp].l}); tb = merge(b, tb); tg[tb]++; val[tb] -= 1; root = merge(merge(ta, a), tb); return k - val[tmp].l + val[tmp].head; } } s[300010], back; int main() { srand(20040826); scanf("%d%d%d", &n, &m, &q); back.len = n; for (long long i = 1; i <= n; i++) back.insert(i * m, i); for (long long i = 1; i <= n; i++) { s[i].len = m - 1; s[i].build((node){1, m - 1, (i - 1) * m + 1}); } int x, y; long long ta, tb; while (q--) { scanf("%d%d", &x, &y); if (y != m) { ta = s[x].nxt(y); tb = back.nxt(x); s[x].insert(tb); back.insert(ta); } else { ta = back.nxt(x); back.insert(ta); } printf("%lld\n", ta); } return 0; }
C++ Code:(UOJ)
#include <cstdio> #include <cstdlib> #define maxn 5000010 using namespace std; struct node{ int l, r; long long head; void operator -= (long long a) {l -= a; r -= a;} } val[maxn]; int n, m, q; int lc[maxn], rc[maxn], num[maxn], sz[maxn], tg[maxn], idx; struct treap { int root, len; int ta, tb, tmp; int nw(node x) { val[++idx] = x; sz[idx] = 1; num[idx] = rand(); lc[idx] = rc[idx] = 0; return idx; } void update(int p) {sz[p] = sz[lc[p]] + sz[rc[p]] + 1;} void pushdown(int p) { int tmp6 = tg[p]; tg[p] = 0; val[lc[p]] -= tmp6; val[rc[p]] -= tmp6; tg[lc[p]] += tmp6; tg[rc[p]] += tmp6; } void splitl(int rt, int k, int &x, int &y) { if (!rt) x = y = 0; else { if (val[rt].l <= k) splitl(rc[rt], k, rc[rt], y), x = rt; else splitl(lc[rt], k, x, lc[rt]), y = rt; update(rt); } } void splitr(int rt, int k, int &x, int &y) { if (!rt) x = y = 0; else { pushdown(rt); if (val[rt].r <= k) splitr(rc[rt], k, rc[rt], y), x = rt; else splitr(lc[rt], k, x, lc[rt]), y = rt; update(rt); } } int merge(int x, int y) { if (!x || !y) return x | y; if (num[x] > num[y]) { pushdown(x); rc[x] = merge(rc[x], y); update(x); return x; } else { pushdown(y); lc[y] = merge(x, lc[y]); update(y); return (y); } } void build(node p) { root = nw(p); } void insert(long long p, int k = -1){ if (k == -1) k = len; if (!root) root = nw((node) {k, k, p}); else root = merge(root, nw((node) {k, k, p})); } long long nxt(int k) { splitr(root, k - 1, ta, tmp); splitl(tmp, k, tmp, tb); int a = nw((node) {val[tmp].l, k - 1, val[tmp].head}), b = nw((node) {k + 1, val[tmp].r, val[tmp].head + k + 1 - val[tmp].l}); tb = merge(b, tb); tg[tb]++; val[tb] -= 1; root = merge(merge(ta, a), tb); return k - val[tmp].l + val[tmp].head; } } s[300010], back; int main() { srand(20040826); scanf("%d%d%d", &n, &m, &q); back.len = n; for (long long i = 1; i <= n; i++) back.insert(i * m, i); for (long long i = 1; i <= n; i++) { s[i].len = m - 1; s[i].build((node){1, m - 1, (i - 1) * m + 1}); } int x, y; long long ta, tb; while (q--) { scanf("%d%d", &x, &y); if (y != m) { ta = s[x].nxt(y); tb = back.nxt(x); s[x].insert(tb); back.insert(ta); } else { ta = back.nxt(x); back.insert(ta); } printf("%lld\n", ta); } return 0; }