CF487E Tourists 做题记录

· · 个人记录

码农题,写线段树给我写爽了,这道题又是图,但是要求路径最小值,所以还是最好要转化成树,所以使用圆方树,而且点双在这道题有很好的性质:方点的点权就是这个点双的最小值,因为点双内两点肯定能够经过第三个点。

所以我们先建好圆方树,敲好树剖,如果没有修改的话,用线段树维护方点代表点双的最小值,再用线段树维护整棵树区间最小值,然后树剖求就可以了。但~~是,有修改就不好做了,话说没修可以用点权重构二叉树吗?

如果修改把该圆点与所有相连的方点全部修改,那我用个菊花图你不就炸了吗,所以这里有个恶作剧:每个方点只统计作为它儿子的点双的点权,而有可能为它父亲的点双的点就不管,这样每次修改只需要修改一个方点,而最后统计答案是如果 LCA 是方点,那再统计它的父亲,即那个本应被统计却没被统计的圆点。题解里面一堆用 multiset 维护方点最小值的,这里用的是动态开点线段树,码量多了不止一倍,不过好写爱写😋

另外记得修改时修改维护整棵线段树时用时间戳表示线段树下标,维护方点最小值用一个数组记录该圆点是第几位下标,其他就没什么了。

//Just Sayori
#include <iostream>
#include <cstdio>
#include <algorithm>
#define ll long long
#define rnt register int
#define gr getchar
#define pr putchar
#define N 500005
#define M 1000000007
using namespace std;

inline ll read()
{
    ll x = 0, f = 1;
    char ch = gr();
    while (ch < '0' || ch > '9')
        ch == '-' ? f = -1, ch = gr() : ch = gr();
    while (ch >= '0' && ch <= '9')
        x = (x << 3) + (x << 1) + (ch ^ 48), ch = gr();
    return x * f;
}

inline void write(ll x)
{
    static int sta[39], top = 0;
    if (x < 0)
        pr('-'), x = -x;
    do
        sta[++top] = x % 10, x /= 10;
    while (x);
    while (top)
        pr(sta[top--] ^ 48);
}

struct Edge
{
    struct edge
    {
        int v, next;
    } e[N << 1];
    int head[N], cnt;
    inline void tdd(int u, int v)
    {
        e[++cnt] = {v, head[u]}, head[u] = cnt;
        e[++cnt] = {u, head[v]}, head[v] = cnt;
    }
} g, t;
int n, m, q, u, v, bcc, cnt, tp;
int dfn[N], low[N], stack[N];
int a[N], w[N], fa[N], sz[N], num[N], pos[N], top[N], bson[N], deep[N], root[N];

void tarjan(int u)
{
    dfn[u] = low[u] = ++cnt;
    stack[++tp] = u;
    for (rnt i = g.head[u]; i; i = g.e[i].next)
    {
        int v = g.e[i].v;
        if (!dfn[v])
        {
            tarjan(v), low[u] = min(low[v], low[u]);
            if (low[v] == dfn[u])
            {
                bcc++;
                while (stack[tp + 1] != v)
                    t.tdd(stack[tp--], bcc);
                t.tdd(u, bcc);
            }
        }
        else
            low[u] = min(low[u], dfn[v]);
    }
}

void dfs1(int u, int f)
{
    fa[u] = f, sz[u] = 1;
    deep[u] = deep[f] + 1;
    for (rnt i = t.head[u]; i; i = t.e[i].next)
    {
        int v = t.e[i].v;
        if (v == f)
            continue;
        dfs1(v, u);
        sz[u] += sz[v];
        if (sz[v] > sz[bson[u]])
            bson[u] = v;
    }
}

void dfs2(int u, int tp)
{
    top[u] = tp;
    dfn[u] = ++cnt;
    w[cnt] = a[u];
    if (!bson[u])
        return;
    dfs2(bson[u], tp);
    for (rnt i = t.head[u]; i; i = t.e[i].next)
    {
        int v = t.e[i].v;
        if (v == fa[u] || v == bson[u])
            continue;
        dfs2(v, v);
    }
}

struct tree
{
    int l, r, min;
} s[N << 2];

void push_up(int u)
{
    s[u].min = M;
    if (s[u << 1].min)
        s[u].min = min(s[u << 1].min, s[u].min);
    if (s[u << 1 | 1].min)
        s[u].min = min(s[u << 1 | 1].min, s[u].min);
}

void build(int u, int l, int r)
{
    s[u].l = l, s[u].r = r;
    if (l == r)
        return s[u].min = w[l], void();
    int mid = (l + r) >> 1;
    build(u << 1, l, mid);
    build(u << 1 | 1, mid + 1, r);
    push_up(u);
}

void modify(int u, int x, int w)
{
    if (s[u].l == s[u].r)
        return s[u].min = w, void();
    int mid = (s[u].l + s[u].r) >> 1;
    if (x <= mid)
        modify(u << 1, x, w);
    else
        modify(u << 1 | 1, x, w);
    push_up(u);
}

int query(int u, int l, int r)
{
    if (l <= s[u].l && s[u].r <= r)
        return s[u].min;
    int mid = (s[u].l + s[u].r) >> 1, ans = M;
    if (l <= mid)
        ans = min(ans, query(u << 1, l, r));
    if (r >= mid + 1)
        ans = min(ans, query(u << 1 | 1, l, r));
    return ans;
}

struct tree2
{
    int lc, rc, min;
} k[N << 2];

void push_upk(int u)
{
    k[u].min = M;
    if (k[k[u].lc].min)
        k[u].min = min(k[k[u].lc].min, k[u].min);
    if (k[k[u].rc].min)
        k[u].min = min(k[k[u].rc].min, k[u].min);
}

void modifyk(int &u, int l, int r, int x, int w)
{
    if (!u)
        u = ++cnt;
    if (l == r)
        return k[u].min = w, void();
    int mid = (l + r) >> 1;
    if (x <= mid)
        modifyk(k[u].lc, l, mid, x, w);
    else
        modifyk(k[u].rc, mid + 1, r, x, w);
    push_upk(u);
}

int get_lca(int a, int b)
{
    int ans = M;
    while (top[a] != top[b])
    {
        if (deep[top[a]] < deep[top[b]])
            swap(a, b);
        ans = min(ans, query(1, dfn[top[a]], dfn[a]));
        a = fa[top[a]];
    }
    if (deep[a] > deep[b])
        swap(a, b);
    ans = min(ans, query(1, dfn[a], dfn[b]));
    if (a > n)
        ans = min(ans, w[dfn[fa[a]]]);
    return ans;
}

int main()
{
    char ch;
    bcc = n = read(), m = read(), q = read();
    for (rnt i = 1; i <= n; i++)
        a[i] = read();
    for (rnt i = 1; i <= m; i++)
        u = read(), v = read(), g.tdd(u, v);
    tarjan(1), cnt = 0, dfs1(1, 0), dfs2(1, 1);
    cnt = 0;
    for (rnt i = 1; i <= n; i++)
        if (fa[i])
            modifyk(root[fa[i]], 1, bcc, ++num[fa[i]], a[i]), pos[i] = num[fa[i]];
    for (rnt i = n + 1; i <= bcc; i++)
        w[dfn[i]] = k[root[i]].min;
    build(1, 1, bcc);
    for (rnt i = 1; i <= q; i++)
    {
        cin >> ch, u = read(), v = read();
        if (ch == 'A')
            write(get_lca(u, v)), pr(10);
        else
        {
            modify(1, dfn[u], v);
            if (fa[u])
            {
                modifyk(root[fa[u]], 1, bcc, pos[u], v);
                modify(1, dfn[fa[u]], k[root[fa[u]]].min);
            }
            w[dfn[u]] = v;
        }
    }
    return 0;
}