题解:P11982 [KTSC 2021] 路灯 / streetlight
题意
给定长度为 \(n\) 的序列 \(a\)。有 \(q\) 次单点修改,在所有修改前和每次修改后,求有多少对 \((i,j)\) 满足:
- \(a_i=a_j\)。
- \(\forall i<k<j,a_k<a_i\)。
\(n\leq 10^5\),\(q\leq 2.5\times 10^5\)。
题解
KTSC 好难啊!!!
为了方便,令 \(a_0=a_{n+1}=\infty\)。考察所有合法的区间 \([i,j]\),我们发现这些区间两两之间要么不交(不考虑端点相交),要么成包含关系,于是这些区间构成了一棵树,根节点为 \([0,n+1]\)。
考察修改操作带来的影响,我们发现单点增加比单点减小性质好很多。因为单点增加 \(a_x\),只会破坏原有的合法区间,并新增极少的区间;而单点减小会导致大量原本不合法的区间突然合并。考虑通过对时间分治全部转化成单点加的情况。具体来说,分治到时间区间 \([L,R]\) 时,把区间内的变化位置设为 \(0\)。考虑 \([mid+1,R]\) 内没有出现在 \([L,mid]\) 内的变化位置,则把这些位置增加到初始值,递归到 \([L,mid]\),然后撤销先前的操作。再考虑 \([L,mid]\) 内没有出现在 \([mid+1,R]\) 内的变化位置,把这些位置增加到 \(mid\) 时刻结束时的值,递归到 \([mid+1,R]\)。
刻画将 \(a_x\) 单点增加到 \(v\) 的影响:
- 删掉所有包含 \(x\) 且满足 \(a_l\leq a_x\) 的区间 \([l,r]\)。
- 考虑 \(x\) 前面第一个 \(\geq v\) 的位置 \(l\),若 \(a_l=v\) 则添加区间 \([l,x]\)。
- 考虑 \(x\) 后面第一个 \(\geq v\) 的位置 \(r\),若 \(a_r=v\) 则添加区间 \([x,r]\)。
放到树上刻画这个事情,可以发现删去的区间是一条祖先链,然后最顶部被删去的区间有可能分裂成新添加的两个区间。
一处细节
考虑对 \(a_x\) 单点加时,如果之前同时存在区间 \([l,x],[x,r]\),则这两个区间都会被删去,那删去的区间就并不是一条祖先链了。如果只是任意位置单点加,这种情况是有可能发生的。但是在我们的分治结构下,这种情况是不会发生的。这是因为,如果我们对某个位置进行了单点加操作,则该位置必然不会在接下来递归下去的区间中出现,这样就不会删去以 \(x\) 为端点的区间了。
考虑直接维护这棵树。区间形成的树结构有一个很好的性质:将所有区间按左端点从小到大排序即可得到这棵树的 DFS 序。因此每次进行一系列单点加操作时,可以把操作序列按操作位置从小到大排序,在 DFS 序上扫描线,用单调栈维护当前点的根链,可以直接找出包含当前点的最小区间,也就是删去的祖先链的链底,然后在单调栈上二分找出删去的祖先链的链顶。线段树二分一下找出新增区间,然后和所有留下来的区间放到一起再排个序就可以了。设树的点数为 \(V\),这样我们可以 \(\mathcal{O}((V+q)\log{V})\) 地处理一个长度为 \(q\) 的操作序列。注意到分治过程中树的大小是没有保证的,因此复杂度可以被卡到 \(\mathcal{O}((n+q)^2\log{(n+q)})\)。
尝试压缩树的点数。考虑比较智慧的一步,对于当前区间内的每个操作,设它会删去树上一条从 \(u\) 到 \(v\) 的祖先链(\(dep_u>dep_v\)),则我们将 \(u\) 和 \(fa_v\) 加入关键点集合中。对关键点建出虚树,那么我们直接维护这个虚树即可。建出虚树后可能会有一些剩余的节点,这些节点一定不会被删去,直接计入答案即可。可以结合下面的图片理解,红点是关键点,蓝边是虚树上的边,橙色区域框起来的点就是剩余节点,我们将其计入 \(cnt\),直接累加到递归区间的答案中。

显然虚树中点数是 \(\mathcal{O}(R-L+1)\) 级别的,这样时间复杂度就优化到了 \(\mathcal{O}((n+q)\log^2(n+q))\)。
建议配合代码理解。实现起来细节很多。
主要代码
int n, q, a[N];
pii op[N + Q];
int lst[N + Q], prv[N + Q], nxt[N + Q];
ll ans[N + Q];
struct Node {
int l, r, v, c;
friend bool operator<(const Node &lhs, const Node &rhs) { return lhs.l < rhs.l; }
friend bool operator==(const Node &lhs, const Node &rhs) { return lhs.l == rhs.l; }
};
Node T[LOGN][N + Q], tmp[N + Q];
int szt[LOGN], sz_tmp;
int sz_upd, upd[N + Q];
int sz_key;
pii key[N + Q];
int top, stk[N + Q];
ll S, sum[N + Q];
struct SegTree {
#define ls(p) (p << 1)
#define rs(p) (p << 1 | 1)
int m, mx[N << 2];
void init() { m = 1 << 32 - __builtin_clz(n - 1); }
void upd(int x, int v) {
mx[x += m - 1] = v;
while (x > 1) x >>= 1, mx[x] = max(mx[ls(x)], mx[rs(x)]);
}
int find_l(int r, int v) {
if (!r) return 0;
r += m - 1;
while (1) {
r >>= __builtin_ctz(~r);
if (mx[r] >= v) {
while (r < m) r = mx[rs(r)] >= v ? rs(r) : ls(r);
return r - m + 1;
}
if (r == lowbit(r)) break;
--r;
}
return 0;
}
int find_r(int l, int v) {
if (l == n + 1) return n + 1;
l += m - 1;
while (1) {
l >>= __builtin_ctz(l);
if (mx[l] >= v) {
while (l < m) l = mx[ls(l)] >= v ? ls(l) : rs(l);
return min(l - m + 1, n + 1);
}
++l;
if (l == lowbit(l)) break;
}
return n + 1;
}
int prv(int x) {
int p = find_l(x - 1, a[x]);
return !p || a[p] > a[x] ? 0 : p;
}
int nxt(int x) {
int p = find_r(x + 1, a[x]);
return p == n + 1 || a[p] > a[x] ? n + 1 : p;
}
#undef ls
#undef rs
} sgt;
void apply_op(int dep) {
Node *T = ::T[dep], *nd = tmp;
int sz_cur = szt[dep], &sz_nxt = sz_tmp;
int p = 0;
top = sz_nxt = 0;
for (int i = 0; i < sz_upd; ++i) {
int x = upd[i];
sgt.upd(x, a[x]);
while (p < sz_cur && T[p].l <= x) {
while (top && T[stk[top]].r <= T[p].r) nd[sz_nxt++] = T[stk[top--]];
stk[++top] = p++;
}
while (top && T[stk[top]].r < x) nd[sz_nxt++] = T[stk[top--]];
while (top && T[stk[top]].v <= a[x]) --top;
}
while (top) nd[sz_nxt++] = T[stk[top--]];
while (p < sz_cur) nd[sz_nxt++] = T[p++];
for (int i = 0; i < sz_upd; ++i) {
int x = upd[i];
int l = sgt.prv(x);
if (l) nd[sz_nxt++] = {l, x, a[x], 1};
int r = sgt.nxt(x);
if (r <= n) nd[sz_nxt++] = {x, r, a[x], 1};
}
sort(nd, nd + sz_nxt), sz_nxt = unique(nd, nd + sz_nxt) - nd;
top = S = 0;
for (int i = 0; i < sz_nxt; ++i) {
while (top && nd[stk[top]].r <= nd[i].r) --top;
sum[i] = (top ? sum[stk[top]] : 0) + nd[i].c;
S += nd[i].c;
stk[++top] = i;
}
}
void build(int dep) {
Node *T = tmp, *nd = ::T[dep + 1];
int sz_cur = sz_tmp, &sz_nxt = szt[dep + 1];
static int V[N + Q << 1];
int p = 0, szv = 0;
V[szv++] = 0;
top = 0;
for (int i = 0; i < sz_key; ++i) {
auto [x, v] = key[i];
while (p < sz_cur && T[p].l <= x) {
while (top && T[stk[top]].r <= T[p].r) --top;
stk[++top] = p++;
}
while (top && T[stk[top]].r < x) --top;
V[szv++] = stk[top];
int l = 1, r = top, mid;
while (l < r) T[stk[mid = l + r + 1 >> 1]].v > v ? l = mid : r = mid - 1;
V[szv++] = stk[l];
}
sort(V, V + szv), szv = unique(V, V + szv) - V;
p = top = 0;
for (int i = 0, sz = szv; i < sz - 1; ++i) {
while (p <= V[i]) {
while (top && T[stk[top]].r <= T[p].r) --top;
stk[++top] = p++;
}
while (top && T[stk[top]].r <= T[V[i + 1]].r) --top;
V[szv++] = stk[top];
}
sort(V, V + szv), szv = unique(V, V + szv) - V;
top = 0, sz_nxt = szv;
for (int i = 0; i < szv; ++i) {
auto [l, r, v, c] = T[V[i]];
while (top && T[stk[top]].r <= r) --top;
c = sum[V[i]] - (top ? sum[stk[top]] : 0);
S -= c;
nd[i] = {l, r, v, c};
stk[++top] = V[i];
}
}
void solve(int l, int r, int dep) {
auto proc = [&]() {
sort(upd, upd + sz_upd), sort(key, key + sz_key);
for (int i = 0; i < sz_key; ++i) sgt.upd(key[i].first, 0);
apply_op(dep), build(dep);
};
if (l == r) {
a[op[l].first] = op[l].second;
upd[sz_upd++] = op[l].first;
proc();
ans[l] += S, ans[l + 1] -= S;
return sz_upd = 0, void();
}
int mid = l + r >> 1;
if (mid >= n) {
for (int i = mid + 1; i <= r; ++i) if (prv[i] < l) upd[sz_upd++] = op[i].first;
copy(op + l, op + mid + 1, key), sz_key = mid - l + 1;
for (int i = l; i <= mid; ++i) if (prv[i] && prv[i] < l) key[sz_key++] = op[prv[i]];
proc();
ans[l] += S, ans[mid + 1] -= S;
sz_upd = sz_key = 0;
solve(l, mid, dep + 1);
}
for (int i = l; i <= mid; ++i) if (nxt[i] > r) upd[sz_upd++] = op[i].first;
copy(op + mid + 1, op + r + 1, key), sz_key = r - mid;
for (int i = mid + 1; i <= r; ++i) if (prv[i] && prv[i] <= mid) key[sz_key++] = op[prv[i]];
proc();
ans[mid + 1] += S, ans[r + 1] -= S;
sz_upd = sz_key = 0;
solve(mid + 1, r, dep + 1);
}

浙公网安备 33010602011771号