[NOIP2017 提高组] 列队
17 年时在考场上见到这题。现在又见到这题,现在会了,那我是不是有一点变强了呢?(苦笑)
首先在我的知识范围内不能整体做变动
考虑对弹出的那一个位置做某种标记,然后在后面什么地方插进去
做什么标记? +1 表示“该处少一个”
树状数组?二维树状数组?那后面插哪?插右下角吗?
想不出来
如果问题是一维呢?那打个标记然后插在最后面
开线段树呢?
每行每列都开一个线段树
考虑一下“看齐”过程,只用每行 1~M-1 一个线段树,最后一列再一个线段树,于是就成了一维的问题
线段树肯定开不下
动态开点
实现细节?
单点打标记,当我们要找到第 p 位置时,实际位置 pp 满足 \(pp-sum[1,pp-1]=p\)
插入的话往对应的 vector 里插
查询的话,如果 pp 还在原来的 N、M 内,直接算出来,超出的话去 vector 里面找
做完了
空间的话,3e5 * lg3e5 / lg2 ,然后按单次操作的常数多开些
// I am SB
#include <vector>
#include <cstdio>
#include <cstring>
#include <iostream>
using namespace std;
typedef long long ll;
const int MAXN = 300005;
const int MAXM = 7000005;
int N, M, Q;
vector<ll> P[MAXN];
struct segmentTree {
#define lson ls[x]
#define rson rs[x]
int tot, ls[MAXM], rs[MAXM], sum[MAXM];
segmentTree() {
memset(ls, 0, sizeof(ls));
memset(rs, 0, sizeof(rs));
memset(sum, 0, sizeof(sum));
}
void pushup(int x) { sum[x] = sum[lson] + sum[rson]; }
void update(int &x, int l, int r, int p) {
if (!x) x = ++tot;
if (l==r) { sum[x]++; }
else {
int mid = (l + r) >> 1;
if (mid>=p) update(lson, l, mid, p);
if (mid< p) update(rson, mid+1, r, p);
pushup(x);
}
}
int query(int x, int l, int r, int k) {
if (l==r) return l;
else {
int mid = (l + r) >> 1;
if (mid-sum[lson]>=k) return query(lson, l, mid, k);
else return query(rson, mid+1, r, k+sum[lson]);
}
}
} ST;
int read()
{
register int o = 0;
register char c = getchar();
while (c< '0' || c> '9') c = getchar();
while (c>='0' && c<='9') o = (o<<3)+(o<<1)+(c&15), c = getchar();
return o;
}
int main()
{
// freopen("in.txt", "r", stdin);
// freopen("ans.txt", "w", stdout);
N = read(), M = read(), Q = read(), ST.tot = N+1;
for (int t=1; Q; Q--, t++) {
int x = read(), y = read();
if (y< M) {
int p = ST.query(x, 1, MAXN<<1, y); ll ans;
if (p< M) ans = (ll) (x-1) * M + p; else ans = P[x][p-M];
ST.update(x, 1, MAXN<<1, p);
int rt = N + 1;
int q = ST.query(rt, 1, MAXN<<1, x);
if (q<=N) P[x].push_back((ll)q*M);
else P[x].push_back(P[N+1][q-N-1]);
ST.update(rt, 1, MAXN<<1, q);
P[N+1].push_back(ans);
printf("%lld\n", ans);
} else {
int q = ST.query(N+1, 1, MAXN<<1, x); ll ans;
//printf(" q = %d\n", q);
if (q<=N) ans = (ll) q * M; else ans = P[N+1][q-N-1];
int rt = N + 1;
ST.update(rt, 1, MAXN<<1, q);
P[N+1].push_back(ans);
printf("%lld\n", ans);
}
}
}
其实想强调的是动态开点
单点操作就是一条链(用 lazytag 的话区间操作也是个 tag )
所以你可以开多个线段树去维护东西,还能线段树合并
所以这玩意也不是说只能做值域线段树。不用 build 的,单点操作的(区间也行),都能用