CF 487E Tourists

题意

给定一张简单无向连通图,要求支持两种操作:

  1. 修改一个点的点权。
  2. 询问两点之间所有简单路径上点权的最小值。

题解

一道比较模板的圆方树题,先建出原图的圆方树,这样问题就转化为了树上的路径问题。

如果我们令方点的权值为相邻圆点的最小值,查询只需要查询路径最小值,只需要树链剖分维护即可。但是这样有一个问题,修改一个圆点权值的时候可能会同时修改 \(O(n)\) 个方点,无法承受。

一个巧妙的做法是,我们令方点的权值为儿子权值的最小值,这样修改的时候只会修改父亲的权值,直接对每个点开 multiset 维护。查询的时候注意如果 lca 是圆点,需要额外查询 lca 父亲的权值。

#include <bits/stdc++.h>

const int N = 2e5 + 10, M = 4e5 + 10, inf = 2147483647;

inline int read()
{
    int x = 0;
    char ch = getchar();
    while (!isdigit(ch)) ch = getchar();
    while (isdigit(ch)) x = x * 10 + ch - '0', ch = getchar();
    return x;
}

std::stack<int> stk;
std::vector<int> G[N];
std::multiset<int> S[N];
int cnt = 0, idx = 0, dfn[N], low[N];
int tot = 1, fir[N], nex[M], got[M];
int Idx = 0, siz[N], son[N], dep[N], par[N], top[N], rev[N], w[N];

struct SegmentTree
{
    int Min[N << 2];
    #define mid ((l + r) >> 1)

    inline void upd(int k)
    {
        Min[k] = std::min(Min[k << 1], Min[k << 1 | 1]);
    }

    inline void build(int k, int l, int r)
    {
        if (l == r) return Min[k] = w[rev[l]], void();
        build(k << 1, l, mid), build(k << 1 | 1, mid + 1, r), upd(k);
    }

    inline void Mod(int k, int l, int r, int p, int dat)
    {
        if (l == r) return Min[k] = dat, void();
        p <= mid ? Mod(k << 1, l, mid, p, dat) : Mod(k << 1 | 1, mid + 1, r, p, dat); upd(k);
    }

    inline int Ask(int k, int l, int r, int ql, int qr)
    {
        if (qr <  l || r <  ql) return inf;
        if (ql <= l && r <= qr) return Min[k];
        return std::min(Ask(k << 1, l, mid, ql, qr), Ask(k << 1 | 1, mid + 1, r, ql, qr));
    }
}T;

inline void AddEdge(int u, int v)
{
    // printf("%d %d\n", u, v);
    nex[++tot] = fir[u], fir[u] = tot, got[tot] = v;
    nex[++tot] = fir[v], fir[v] = tot, got[tot] = u;
}

inline void dfs(int u)
{
    dfn[u] = low[u] = ++idx, stk.push(u);
    for (int v: G[u]) if (!dfn[v])
    {
        dfs(v), low[u] = std::min(low[u], low[v]);
        if (low[v] == dfn[u])
        {
            ++cnt;
            for (int x = 0; x != v; stk.pop())
                x = stk.top(), AddEdge(x, cnt);
            AddEdge(u, cnt);
        }
    }
    else low[u] = std::min(low[u], dfn[v]);
}

inline void dfs(int u, int fa)
{
    dep[u] = dep[par[u] = fa] + 1, siz[u] = 1; 
    for (int i = fir[u]; i; i = nex[i]) if (got[i] != fa)
    {
        dfs(got[i], u), siz[u] += siz[got[i]];
        if (siz[got[i]] > siz[son[u]]) son[u] = got[i];
    }
}

inline void Dfs(int u, int fa)
{
    dfn[u] = ++Idx, rev[Idx] = u;
    top[u] = u == son[fa] ? top[fa] : u;
    if (son[u]) Dfs(son[u], u);
    for (int i = fir[u]; i; i = nex[i])
        if (got[i] != fa && got[i] != son[u]) Dfs(got[i], u);
}

int main()
{
    int n = read(), m = read(), q = read();
    for (int i = 1; i <= n; ++i) w[i] = read();
    for (int i = 1; i <= m; ++i)
    {
        int u = read(), v = read();
        G[u].push_back(v), G[v].push_back(u);
    }
    cnt = n, dfs(1), dfs(1, 0), Dfs(1, 0);
    for (int i = 2; i <= n; ++i) S[par[i]].insert(w[i]);
    for (int i = n + 1; i <= cnt; ++i) w[i] = *S[i].begin();
    T.build(1, 1, cnt);
    for (int i = 1; i <= q; ++i)
    {
        int opt = getchar();
        while (opt != 'C' && opt != 'A') opt = getchar();
        if (opt == 'C')
        {
            int u = read(), dat = read();
            T.Mod(1, 1, cnt, dfn[u], dat);
            if (par[u])
            {
                S[par[u]].erase(S[par[u]].lower_bound(w[u])), S[par[u]].insert(dat);
                if (w[par[u]] != *S[par[u]].begin()) 
                    T.Mod(1, 1, cnt, dfn[par[u]], w[par[u]] = *S[par[u]].begin());
            }
            w[u] = dat;
        }
        else
        {
            int u = read(), v = read(), ans = inf;
            for (int fu = top[u], fv = top[v]; fu != fv; fu = top[u = par[fu]])
            {
                if (dep[fu] < dep[fv]) std::swap(fu, fv), std::swap(u, v);
                ans = std::min(ans, T.Ask(1, 1, cnt, dfn[fu], dfn[u]));
            }
            if (dep[u] > dep[v]) std::swap(u, v);
            ans = std::min(ans, T.Ask(1, 1, cnt, dfn[u], dfn[v]));
            printf("%d\n", std::min(ans, u > n ? w[par[u]] : inf));
        }
    }
    return 0;
}

posted @ 2020-10-11 19:19  bo1949  阅读(85)  评论(0)    收藏  举报