题解:P13129 [Ynoi Easy Round 2019] 有马加奈

题意

给定一棵 \(n\) 个点的树,节点 \(i\) 上有 \((a_i,b_i)\) 二元组,初始时为 \((0,0)\)\(q\) 次操作:

  1. 给定 \(x,c\),设当前是第 \(t\) 次操作,对于 \(x\) 到根节点的路径上的每个点 \(i\),若 \(a_i\neq c\) 则令 \((a_i,b_i)\leftarrow (c,t)\),否则不变。
  2. 给定 \(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;
}
posted @ 2025-08-25 21:28  P2441M  阅读(15)  评论(0)    收藏  举报