【NOIP2017】【UOJ334】【LOJ2319】列隊
阿新 • • 發佈:2019-02-16
【題目連結】
【前置技能】
- 動態開節點線段樹
【題解】
- 本題好像有什麼樹狀陣列就能做的解法,但我只會線段樹和Splay的大力亂搞,畢竟思考起來比較直觀一點。以下是線段樹做法,程式碼比較醜,懶得改了。
- 仔細觀察發現一次變動影響的點不是很多,那我們就建立支援單點修改的線段樹。對每一行的前個點維護兩棵線段樹,對最後一列維護兩棵線段樹。其中第一棵線段樹表示的是原來的人,第二棵線段樹表示的是修改後進來的人。一開始兩棵線段樹都是空的,表示每個位置上都是原來的人。
- 在某一個人移動的時候,先在第一棵線段樹上看一下未移動的人是否大於等於個,若滿足,則在第一棵線段樹上二分找到第
- 注意移動的人就在最後一列的情況要特判一下,直接在維護列的線段樹上修改即可。
- 因為每次最多修改個節點,動態開節點,空間複雜度。時間複雜度。
【程式碼】
#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;
}