Loading

【题解】CF1083C Max Mex

题意

给定一棵带权树,所有结点的点权是 \(0\)\(n - 1\) 的排列。

每次操作可以:

  1. 交换两个结点的点权

  2. 询问树上任意路径权值 \(Mex\) 的最大值

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

思路

线段树。

线段树可以解决的不仅是区间问题,只要问题满足可合并性都能用线段树做。

\(Mex\) 相当于求一个最大的自然数 \(x\),使得点权为 \([0, x)\) 的点都在同一条路径上。

我们发现这个区间是连续的,可以很好地用线段树做。

假设现在需要知道 \([l, r]\) 的点权是否在同一路径上,令 \(m = \lfloor \frac{l + r}{2} \rfloor\),此时已经知道 \([l, m]\)\((m, r]\) 的答案。

显然左右两半的区间都必须在同一路径上。

不妨令这两条路径分别是 \((p, q), (a, b)\)。如果存在一条最短的路径包含 \([l, r]\) 的所有点权,那么这条路径的端点只有可能在 \(p, q, a, b\) 中取。

显然 \(q, a\) 在路径 \((a, b)\) 上的充要条件是 \(dis(p, q) + dis(q, b) = dis(p, a) + dis(a, b) = dis(p, b)\),于是可以用欧拉序 + ST 表 \(O(1)\) 做。每次线段树合并的时候就只需要枚举端点即可。

修改直接在线段树上做。

查询可以用线段树二分。假设现在已经知道在同一路径上的点权值域 \(V\)。因为线段树二分是按照中序遍历区间,所以可以直接尝试将整个左半区间加入 \(V\) 然后相应递归,顺序是正确的。

代码

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

const int maxn = 2e5 + 5;
const int maxe = 2e5 + 5;

struct node
{
    int to, nxt;
} edge[maxe];

int n, q;
int cnt, ans;
int a[maxn], pos[maxn];
int head[maxn], dfn[maxn], dep[maxn];

namespace ST
{
    int len;
    int lg[maxn << 1], f[maxn << 1][20];

    int get_min(int a, int b) { return (dep[a] < dep[b] ? a : b); }

    void init()
    {
        for (int i = 2; i <= len; i++) lg[i] = lg[i >> 1] + 1;
        for (int j = 1; (1 << j) <= len; j++)
            for (int i = 1; i + (1 << j) - 1 <= len; i++)
                f[i][j] = get_min(f[i][j - 1], f[i + (1 << (j - 1))][j - 1]);
    }

    int query(int l, int r)
    {
        if (l > r) swap(l, r);
        int k = lg[r - l + 1];
        return get_min(f[l][k], f[r - (1 << k) + 1][k]);
    }
}

int lca(int a, int b) { return ST::query(dfn[a], dfn[b]); }

int dis(int a, int b) { return dep[a] + dep[b] - 2 * dep[lca(a, b)]; }

bool check(int p, int q, int a, int b)
{
    int d = dis(p, b);
    return (dis(p, q) + dis(q, b) == d) && (dis(p, a) + dis(a, b) == d);
}

namespace SGT
{
    struct node
    {
        int u, v;
    } tree[maxn << 2], ret;

    void push_up(node &cur, node &l, node &r)
    {
        if ((!l.u) || (!r.u)) cur.u = cur.v = 0;
        else if (check(l.u, l.v, r.u, r.v)) cur.u = l.u, cur.v = r.v;
        else if (check(l.v, l.u, r.u, r.v)) cur.u = l.v, cur.v = r.v;
        else if (check(l.u, l.v, r.v, r.u)) cur.u = l.u, cur.v = r.u;
        else if (check(l.v, r.v, l.u, r.u)) cur.u = l.v, cur.v = r.u;
        else if (check(l.u, r.u, r.v, l.v)) cur.u = l.u, cur.v = l.v;
        else if (check(r.u, l.u, l.v, r.v)) cur.u = r.u, cur.v = r.v;
        else cur.u = cur.v = 0;
    }

    void build(int k, int l, int r)
    {
        if (l == r)
        {
            tree[k].u = tree[k].v = pos[l];
            return;
        }
        int mid = (l + r) >> 1;
        build(k << 1, l, mid);
        build(k << 1 | 1, mid + 1, r);
        push_up(tree[k], tree[k << 1], tree[k << 1 | 1]);
    }

    void update(int k, int l, int r, int p, int w)
    {
        if (l == r)
        {
            tree[k].u = tree[k].v = w;
            return;
        }
        int mid = (l + r) >> 1;
        if (p <= mid) update(k << 1, l, mid, p, w);
        else update(k << 1 | 1, mid + 1, r, p, w);
        push_up(tree[k], tree[k << 1], tree[k << 1 | 1]);
    }

    bool modify(node nd, int len)
    {
        if (!ans)
        {
            ans = len, ret = nd;
            return true;
        }
        node tmp;
        push_up(tmp, ret, nd);
        if (!tmp.u) return false;
        ans += len, ret = tmp;
        return true;
    }

    void query(int k, int l, int r)
    {
        // printf("%d, %d, %d\n", l, r, k);
        if (l == r)
        {
            modify(tree[k], 1);
            return;
        }
        int mid = (l + r) >> 1;
        if (tree[k].u && modify(tree[k], r - l + 1)) return;
        // if ((!ans) && (tree[k << 1].u)) puts("disadiajd");
        if (tree[k << 1].u && modify(tree[k << 1], mid - l + 1)) query(k << 1 | 1, mid + 1, r);
        else query(k << 1, l, mid);
    }
}

void add_edge(int u, int v)
{
    cnt++;
    edge[cnt].to = v;
    edge[cnt].nxt = head[u];
    head[u] = cnt;
}

void dfs(int u, int fa)
{
    dep[u] = dep[fa] + 1;
    ST::f[++ST::len][0] = u;
    dfn[u] = ST::len;
    for (int i = head[u]; i; i = edge[i].nxt)
    {
        int v = edge[i].to;
        dfs(v, u);
        ST::f[++ST::len][0] = u;
    }
}

int main()
{
    // freopen("CF1083C_0.in", "r", stdin);
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", &a[i]), a[i]++;
        pos[a[i]] = i;
    }
    for (int i = 2, f; i <= n; i++)
    {
        scanf("%d", &f);
        add_edge(f, i);
    }
    cnt = 0;
    dfs(1, 0);
    ST::init();
    SGT::build(1, 1, n);
    scanf("%d", &q);
    while (q--)
    {
        int opt;
        scanf("%d", &opt);
        if (opt == 1)
        {
            int u, v;
            scanf("%d%d", &u, &v);
            SGT::update(1, 1, n, a[v], u);
            SGT::update(1, 1, n, a[u], v);
            swap(a[u], a[v]);
        }
        else
        {
            ans = 0;
            SGT::query(1, 1, n);
            printf("%d\n", ans);
        }
    }
    return 0;
}

/*
1, 6, 1
1, 3, 2
1, 2, 4
2, 2, 9

1, 6, 1
4, 6, 3
4, 5, 6
4, 4, 12
*/
posted @ 2023-01-05 15:05  kymru  阅读(21)  评论(0编辑  收藏  举报