题解:P13129 [Ynoi Easy Round 2019] 有马加奈
题意
给定一棵 \(n\) 个点的树,节点 \(i\) 上有 \((a_i,b_i)\) 二元组,初始时为 \((0,0)\)。\(q\) 次操作:
- 给定 \(x,c\),设当前是第 \(t\) 次操作,对于 \(x\) 到根节点的路径上的每个点 \(i\),若 \(a_i\neq c\) 则令 \((a_i,b_i)\leftarrow (c,t)\),否则不变。
- 给定 \(x\),查询 \((a_x,b_x)\)。
\(1\leq n,q\leq 10^6\)。
题解
我好菜啊。
对于每个二元组 \((a_i,b_i)\),把 \(a_i\) 视作颜色,那么对于某个时刻 \(t\),\(b_i\) 的就是最小的时刻使得点 \(i\) 在 \([b_i,t]\) 时刻内,颜色始终是 \(a_i\)。
离线,把每个操作 \(1\) 挂在 \(x\) 上,DFS 整棵树转化为维护子树内信息。
对每个点在时间轴上开一个动态开点线段树,节点 \([l,r]\) 维护 \(col\) 表示该点在 \(r\) 时刻的颜色,\(st\) 表示最小的时刻使得该点在 \([st,r]\) 时刻内,颜色始终是 \(col\),\(same\) 表示 \(st\) 是否等于 \(l\),即该点是否在时间区间内保持同色。节点很容易 \(\mathcal{O}(1)\) 合并。
DFS 到一个点的时候,加入当前点的修改操作,然后和儿子节点的线段树做线段树合并即可。查询时刻 \(t\) 的二元组只需在线段树上做前缀查询即可。
时间复杂度 \(\mathcal{O}(q\log{q})\)。
被树剖吊打了。
代码
#include <iostream>
#include <vector>
using namespace std;
#define lowbit(x) ((x) & -(x))
#define chk_min(x, v) (x) = min((x), (v))
#define chk_max(x, v) (x) = max((x), (v))
typedef long long ll;
typedef pair<int, int> pii;
const int N = 1e6 + 5, N2 = N << 3;
namespace IO {
const int S = 1 << 24, lm = 1 << 23;
char bi[S + 5], bo[S + 5], *p1 = bi, *p2 = bi, *p3 = bo, ch;
int s, bts, szo;
inline char gc() { return p1 == p2 && (p2 = (p1 = bi) + fread(bi, 1, 1 << 23, stdin), p1 == p2) ? EOF : *p1++; }
inline ll rd() {
s = 1, bts = 0; char ch;
while (ch = gc(), (ch < '0'));
ll x = ch ^ 48;
while (ch = gc(), (ch >= '0')) x = (x << 3) + (x << 1) + (ch ^ 48);
return s == 1 ? x : -x;
}
}
using IO::rd;
int n, q, rt[N], fa[N];
bool vis[N];
pii ans[N];
struct Op { int t, c; };
vector<Op> ops[N];
vector<int> qr[N];
struct Node {
int st, col;
bool same;
Node(int s = 0, int c = 0, bool sm = 1) : st(s), col(c), same(sm) {}
Node operator+(const Node &x) const {
if (!col) return x;
if (!x.col) return *this;
if (col == x.col && x.same) return *this;
return {x.st, x.col, 0};
}
};
struct SegTree {
int tot, ls[N2], rs[N2];
Node nodes[N2];
inline void push_up(int p) { nodes[p] = nodes[ls[p]] + nodes[rs[p]]; }
inline void upd(int &p, int l, int r, int x, int c) {
if (!p) p = ++tot;
if (l == r) return nodes[p] = {x, c, 1}, void();
int mid = l + r >> 1;
x <= mid ? upd(ls[p], l, mid, x, c) : upd(rs[p], mid + 1, r, x, c);
push_up(p);
}
inline Node query(int p, int l, int r, int x, int y) {
if (!p) return {};
if (x <= l && y >= r) return nodes[p];
int mid = l + r >> 1;
Node res;
if (x <= mid) res = query(ls[p], l, mid, x, y);
if (y > mid) res = res + query(rs[p], mid + 1, r, x, y);
return res;
}
inline int merge(int p, int q, int l, int r) {
if (!p || !q) return p | q;
int mid = l + r >> 1;
ls[p] = merge(ls[p], ls[q], l, mid), rs[p] = merge(rs[p], rs[q], mid + 1, r);
return push_up(p), p;
}
} sgt;
int main() {
ios::sync_with_stdio(0), cin.tie(0);
n = rd(), q = rd();
for (int i = 2; i <= n; ++i) fa[i] = rd();
for (int i = 1; i <= q; ++i) {
int op = rd(), x = rd(), c;
if (op == 1) ops[x].push_back({i, c = rd()});
else qr[x].push_back(i), vis[i] = 1;
}
for (int i = n; i; --i) {
int x = i;
for (Op &op : ops[x]) sgt.upd(rt[x], 1, q, op.t, op.c);
Node res;
for (int t : qr[x]) res = sgt.query(rt[x], 1, q, 1, t), ans[t] = {res.col, res.st};
if (fa[x]) rt[fa[x]] = sgt.merge(rt[fa[x]], rt[x], 1, q);
}
for (int i = 1; i <= q; ++i) if (vis[i]) cout << ans[i].first << ' ' << ans[i].second << '\n';
return 0;
}

浙公网安备 33010602011771号