彩色圣诞树 题解

前言

笑点解析:特殊性质 \(t\) 判错了,挂了 \(15\)pts。

题意简述

一棵 \(n\) 个结点的有根树,\(1\) 为树根,初始每个点都有不同的颜色。在接下来 \(m\) 次操作中,你需要维护这棵树,有如下 \(3\) 种操作:

  1. \(u\) 到根这条链上每一个点的颜色覆盖为新的一种颜色;
  2. 查询 \(f(u, v)\),其中 \(f(u, v)\) 表示 \(u\)\(v\) 链上不同颜色数;
  3. 对于一个 \(u\) 子树中的 \(v\),查询 \(\max \limits _ {v \in \operatorname{subtree}(u)} f(1, v)\)

\(n, m \leq 2 \times 10 ^ 5\)

题目分析

由于每次覆盖都是新开一个颜色,所以我们猜想 \(f(u, v)\)\(u\)\(v\) 链上颜色段数是相同的。

点击展开证明

考虑一种颜色,最初其在树上出现总是一条链,且这条链的一端为另一端的祖先。考虑影响到这种颜色,当且仅当另一种颜色把链顶到链中某一个点(或者链底)覆盖了。所以每种颜色在任何时刻,在树上都是以一条连续的链存在。\(f(u, v)\) 不等于颜色段数,当且仅当有一种颜色多贡献了颜色段数(即在 \(u\)\(v\) 间出现的形式为至少两段),而我们证明了一种颜色时刻都是连续的一条链,所以这是不可能的。

诶诶诶,这不就成 原题 了吗?甚至还是弱化版?你别急,你先别急,还有操作 \(3\) 呢。

操作 \(3\) 要求我们维护 \(f(1, u)\),并且能够支持子树查询 \(\max\)。如果把树拍到 dfs 序上,查询简单,考虑如何修改。

首先颜色段不好统计,根据经典小 trick,转化为统计 \(1 \sim u\) 有多少 \(\operatorname{color}(v) \neq \operatorname{color}({\operatorname{fa}(v)})\),成了 \(v\) 上的一个点权。当然 \(1\) 的点权总是为 \(1\)

有个暴力的想法,沿用之前树剖 + 线段树的方式,我们新开一棵树。覆盖 \(1 \sim u\),就在树上二分出第一个 \(\operatorname{color}(v) \neq \operatorname{color}({\operatorname{fa}(v)})\)\(v\),将原先和 \(\operatorname{fa}(v)\) 具有相同颜色的 \(v' \in \operatorname{son}(\operatorname{fa}(v))\) 的子树做区间减一,把 \(v\) 的子树做区间加一即可。边界细节处理一下就可以了。树上二分和区间加一只 \(\log\) 没什么要说的,我们需要考虑会存在多少个 \(v\)

事实上,我们猜测这样的 \(v\) 不多,均摊下来,一个可能的上界是 \(\mathcal{O}(\log n)\)

这个结论未经证明,作者想了好久未能构造出比 \(\mathcal{O}(n \log n)\) 更劣的数据。欢迎交流讨论!

点击展开构造方式

考虑满二叉树,相当于一个 01-trie,每次往下走的时候,往和上一次访问到这个结点不同的那个方向,这样一定走满 \(\Theta(\log n)\) 层来到叶子结点。

#include <cstdio>
#include <iostream>
#include <vector>
#include <random>
#include <algorithm>
#include <tuple>
#include <cassert>
using namespace std;

constexpr int n = 200000, m = 200000;
constexpr int p = __lg(n + 1);
constexpr int q = (1 << p) - 1;
constexpr int l = 1 << (p - 1);

vector<pair<int, int>> edges;
vector<tuple<int, int, int>> qrys, q1, q2;

mt19937 rnd(101108);

inline int rand(int l, int r) {
    uniform_int_distribution<int> dist(l, r);
    return dist(rnd);
}

int ls[n + 1], rs[n + 1];
bool lst[n + 1];

signed main() {
    freopen("yzh", "w", stdout);
    
    for (int u = 1; u <= q - l; ++u) {
        ls[u] = u << 1, rs[u] = u << 1 | 1;
        edges.emplace_back(u, ls[u]);
        edges.emplace_back(u, rs[u]);
    }
    for (int u = q + 1; u <= n; ++u) {
        int v;
        do v = rand(q - l + 1, u - 1); while (ls[v] && rs[v]);
        if (!ls[v] && !rs[v]) (rnd() & 1 ? ls : rs)[v] = u;
        else if (!ls[v]) ls[v] = u;
        else rs[v] = u;
        edges.emplace_back(u, v);
    }
    shuffle(edges.begin(), edges.end(), rnd);
    
    int leaf = 0;
    for (int i = 1; i <= n; ++i) leaf += !ls[i] && !rs[i];
    int t = max(1, m / leaf - rand(0, 2));
    for (int _ = 1; _ <= t; ++_) {
        for (int __ = 1; __ <= leaf; ++__) {
            int u = 1;
            while (ls[u] || rs[u]) {
                if (!rs[u] || (lst[u] && ls[u]))
                    lst[u] = false, u = ls[u];
                else
                    lst[u] = true, u = rs[u];
            }
            q1.emplace_back(1, u, 0);
        }
    }
    for (int _ = t * leaf + 1; _ <= m; ++_) {
        int op = rand(2, 3);
        if (op == 2) {
            q2.emplace_back(2, rand(1, n), rand(1, n));
        } else {
            q2.emplace_back(3, rand(1, n), 0);
        }
    }
    
    auto it1 = q1.begin(), it2 = q2.begin();
    for (int _ = 1; _ <= m; ++_) {
        if (it1 != q1.end() && it2 != q2.end()) {
            int $1 = q1.end() - it1, $2 = q2.end() - it2;
            if (rand(1, $1 + $2) <= $1)
                qrys.emplace_back(*it1++);
            else
                qrys.emplace_back(*it2++);
        } else if (it2 == q2.end())
            qrys.emplace_back(*it1++);
        else
            qrys.emplace_back(*it2++);
    }
    
    assert((int)edges.size() == n - 1);
    assert((int)qrys.size() == m);
    
    printf("%d %d %d\n", n, m, 0);
    for (auto [u, v]: edges) {
        if (rnd() & 1) swap(u, v);
        printf("%d %d\n", u, v);
    }
    for (auto [op, u, v]: qrys) {
        if (op == 2) printf("%d %d %d\n", op, u, v);
        else printf("%d %d\n", op, u);
    }
    return 0;
}

代码

#include <iostream>
#include <cstdio>
#include <vector>
using namespace std;

const int MAX = 1 << 26;
char buf[MAX], *p = buf, obuf[MAX], *op = obuf;
#ifdef XuYueming
# define fread(_, __, ___, ____)
#else
# define getchar() *p++
#endif
#define putchar(x) *op++ = x
#define isdigit(x) ('0' <= x && x <= '9')
#define __yzh__(x) for (; x isdigit(ch); ch = getchar())
template <typename T>
inline void read(T &x) {
    x = 0; char ch = getchar(); __yzh__(!);
    __yzh__( ) x = (x << 3) + (x << 1) + (ch ^ 48);
}
template <typename T>
inline void write(T x) {
    static short stack[10], top(0);
    do stack[++top] = x % 10; while (x /= 10);
    while (top) putchar(stack[top--] | 48);
    putchar('\n');
}

const int N = 200010;

int n, m, ccnt;
int tp[N << 1], tl[N << 1];
vector<int> edge[N];

int son[N], siz[N], dpt[N];
int top[N], fa[N];
int L[N], R[N], timer;
int dfn[N];

void dfs(int now) {
    siz[now] = 1;
    for (int to: edge[now]) if (to != fa[now]) {
        fa[to] = now, dpt[to] = dpt[now] + 1;
        dfs(to), siz[now] += siz[to];
        if (siz[to] > siz[son[now]]) son[now] = to;
    }
}

void redfs(int now, int tp) {
    dfn[L[now] = ++timer] = now;
    top[now] = tp;
    if (son[now]) redfs(son[now], tp);
    for (int to: edge[now]) if (to != fa[now] && to != son[now]) redfs(to, to);
    R[now] = timer;
}

namespace $1 {
    struct Segment_Tree {
        #define lson (idx << 1    )
        #define rson (idx << 1 | 1)
        
        struct Info {
            int l, r, cnt;
            friend Info operator + (const Info& a, const Info& b) {
                return { a.l, b.r, a.cnt + b.cnt - (a.r == b.l) };
            }
            inline Info swap() const {
                return { r, l, cnt };
            }
        };
        
        struct node {
            int l, r;
            int tag;
            Info info;
        } tree[N << 2];
        
        void build(int idx, int l, int r) {
            tree[idx] = { l, r, -1, { 0, 0, 0 } };
            if (l == r) {
                ++ccnt;
                tree[idx].info = { ccnt, ccnt, 1 };
                tp[ccnt] = tl[ccnt] = dfn[l];
                return;
            }
            int mid = (l + r) >> 1;
            build(lson, l, mid);
            build(rson, mid + 1, r);
            tree[idx].info = tree[lson].info + tree[rson].info;
        }
        
        void pushtag(int idx, int tag) {
            tree[idx].tag = tag;
            tree[idx].info = {tag, tag, 1};
        }
        
        void pushdown(int idx) {
            if (tree[idx].tag == -1) return;
            pushtag(lson, tree[idx].tag);
            pushtag(rson, tree[idx].tag);
            tree[idx].tag = -1;
        }
        
        void modify(int idx, int l, int r, int tag) {
            if (tree[idx].l > r || tree[idx].r < l) return;
            if (l <= tree[idx].l && tree[idx].r <= r) return pushtag(idx, tag);
            pushdown(idx);
            modify(lson, l, r, tag);
            modify(rson, l, r, tag);
            tree[idx].info = tree[lson].info + tree[rson].info;
        }
        
        Info query(int idx, int l, int r) {
            if (l <= tree[idx].l && tree[idx].r <= r) return tree[idx].info;
            pushdown(idx);
            if (r <= tree[lson].r) return query(lson, l, r);
            if (l >= tree[rson].l) return query(rson, l, r);
            return query(lson, l, r) + query(rson, l, r);
        }
        
        int query(int p) {
            return query(1, p, p).l;
        }
        
        #undef lson
        #undef rson
    } yzh;

    inline int lca(int u, int v) {
        while (top[u] != top[v]) {
            if (dpt[top[u]] < dpt[top[v]]) swap(u, v);
            u = fa[top[u]];
        }
        return dpt[u] < dpt[v] ? u : v;
    }

    inline int jump(int u, int p) {
        int res = u;
        while (top[u] != top[p])
            res = top[u], u = fa[top[u]];
        return u == p ? res : dfn[L[p] + 1];
    }

    inline void modify(int u, int v, int c) {
        while (top[u] != top[v]) {
            if (dpt[top[u]] < dpt[top[v]]) swap(u, v);
            yzh.modify(1, L[top[u]], L[u], c);
            u = fa[top[u]];
        }
        if (dpt[u] > dpt[v]) swap(u, v);
        yzh.modify(1, L[u], L[v], c);
    }

    inline Segment_Tree::Info query(int u, int v, bool) {
        Segment_Tree::Info res = {-1, -1, 0};
        while (top[u] != top[v]) {
            res = res + yzh.query(1, L[top[u]], L[u]).swap();
            u = fa[top[u]];
        }
        res = res + yzh.query(1, L[v], L[u]).swap();
        return res;
    }

    inline int query(int u, int v) {
        int p = lca(u, v);
        if (dpt[u] > dpt[v]) swap(u, v);
        if (u == p) {
            Segment_Tree::Info ans = query(v, u, true);
            return ans.cnt;
        } else {
            int q = jump(u, p);
            Segment_Tree::Info ans = query(u, q, true) + query(v, p, true).swap();
            return ans.cnt;
        }
    }
}

namespace $2 {
    struct Segment_Tree {
        #define lson (idx << 1    )
        #define rson (idx << 1 | 1)
        
        struct node {
            int l, r;
            int tag;
            int mx;
        } tree[N << 2];
        
        void pushup(int idx) {
            tree[idx].mx = max(tree[lson].mx, tree[rson].mx);
        }
        
        void build(int idx, int l, int r) {
            tree[idx] = { l, r, 0, 0 };
            if (l == r) return tree[idx].mx = dpt[dfn[l]] + 1, void();
            int mid = (l + r) >> 1;
            build(lson, l, mid);
            build(rson, mid + 1, r);
            pushup(idx);
        }
        
        void pushtag(int idx, int tag) {
            tree[idx].tag += tag;
            tree[idx].mx += tag;
        }
        
        void pushdown(int idx) {
            if (tree[idx].tag == 0) return;
            pushtag(lson, tree[idx].tag);
            pushtag(rson, tree[idx].tag);
            tree[idx].tag = 0;
        }
        
        void modify(int idx, int l, int r, int tag) {
            if (tree[idx].l > r || tree[idx].r < l) return;
            if (l <= tree[idx].l && tree[idx].r <= r) return pushtag(idx, tag);
            pushdown(idx);
            modify(lson, l, r, tag);
            modify(rson, l, r, tag);
            pushup(idx);
        }
        
        int query(int idx, int l, int r) {
            if (l > tree[idx].r || r < tree[idx].l) return 0;
            if (l <= tree[idx].l && tree[idx].r <= r) return tree[idx].mx;
            pushdown(idx);
            return max(query(lson, l, r), query(rson, l, r));
        }
        
        #undef lson
        #undef rson
    } yzh;
    
    void modify(int u, int c) {
        tp[c] = 1, tl[c] = u;
        for (int v; u; u = v) {
            int cu = $1::yzh.query(L[u]);
            v = fa[tp[cu]];
            if (v) yzh.modify(1, L[tp[cu]], R[tp[cu]], -1);
            if (u != tl[cu]) {
                int p = $1::jump(tl[cu], u);
                yzh.modify(1, L[p], R[p], 1);
                tp[cu] = p;
            }
        }
    }
    
    inline int query(int u) {
        return yzh.query(1, L[u], R[u]);
    }
}

signed main() {
    freopen("yzh", "r", stdin);
    freopen("xym", "w", stdout);
    fread(buf, 1, MAX, stdin);
    read(n), read(m), read(*new int);
    for (int i = 1, u, v; i < n; ++i) read(u), read(v), edge[u].push_back(v), edge[v].push_back(u);
    dfs(1), redfs(1, 1), $1::yzh.build(1, 1, n), $2::yzh.build(1, 1, n);
    for (int i = 1, op, u, v; i <= m; ++i) {
        read(op), read(u);
        if (op == 1) {
            ++ccnt;
            $2::modify(u, ccnt);
            $1::modify(1, u, ccnt);
        } else if (op == 2) {
            read(v);
            write($1::query(u, v));
        } else {
            write($2::query(u));
        }
    }
    fwrite(obuf, 1, op - obuf, stdout);
    return 0;
}
posted @ 2024-10-10 11:13  XuYueming  阅读(40)  评论(0编辑  收藏  举报