「Luogu P3313」[SDOI2014]旅行

\(S\) 国有 \(N\) 个城市,编号从 \(1\)\(N\) 。城市间用 \(N - 1\) 条双向道路连接,满足从一个城市出发可以到达其它所有城市。每个城市信仰不同的宗教,如飞天面条神教、隐形独角兽教、绝地教都是常见的信仰。

为了方便,我们用不同的正整数代表各种宗教, S国的居民常常旅行。旅行时他们总会走最短路,并且为了避免麻烦,只在信仰和他们相同的城市留宿。当然旅程的终点也是信仰与他相同的城市。S国政府为每个城市标定了不同的旅行评级,旅行者们常会记下途中(包括起点和终点)留宿过的城市的评级总和或最大值。

在S国的历史上常会发生以下几种事件:

CC x c:城市 \(x\) 的居民全体改信了 \(c\) 教;

CW x w:城市 \(x\) 的评级调整为 \(w\) ;

QS x y:一位旅行者从城市 \(x\) 出发,到城市 \(y\) ,并记下了途中留宿过的城市的评级总和;

QM x y:一位旅行者从城市 \(x\) 出发,到城市 \(y\) ,并记下了途中留宿过的城市的评级最大值。

由于年代久远,旅行者记下的数字已经遗失了,但记录开始之前每座城市的信仰与评级,还有事件记录本身是完好的。请根据这些信息,还原旅行者记下的数字。 为了方便,我们认为事件之间的间隔足够长,以致在任意一次旅行中,所有城市的评级和信仰保持不变。

Luogu

分析

如果没有宗教属性,那么这道题是一道裸的树链剖分,但是有了它,该怎么办?给每个宗教建一棵线段树?空间肯定爆炸。于是我们可以参考主席树的思想,动态开点,然后用每个结点更新它对应的宗教的线段树,而修改操作,如果是修改宗教,我们还需要删除操作,其他的都是常规操作。

一些闲话。辣鸡 \(strcmp\) ,害我 MLE 了无数发。

代码

#include <bits/stdc++.h>

#define N 100003
#define rg register

using namespace std;

int gi() {
    rg int x = 0, f = 1; rg char c = getchar();
    for ( ; !isdigit(c); c = getchar()) if (c == '-') f = -1;
    for ( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
    return x * f;
}

void chkmax(int &x, int y) { if (x < y) x = y; }

struct SegmentTree {
    int ls, rs, sum, mx;
} t[N << 5];

int n, q, tot, num;
int w[N], c[N], rt[N];
int to[N << 1], nxt[N << 1], hd[N], cnt;
int dep[N], fa[N], dfn[N], rk[N], sz[N], son[N], top[N];

void insert(int u, int v) { to[++cnt] = v, nxt[cnt] = hd[u], hd[u] = cnt; }

void dfs1(int u, int f) {
    dep[u] = dep[f] + 1, fa[u] = f, sz[u] = 1;
    for (int i = hd[u]; i; i = nxt[i]) {
        int v = to[i];
        if (v == f) continue;
        dfs1(v, u);
        sz[u] += sz[v];
        if (sz[v] > sz[son[u]]) son[u] = v;
    }
}

void dfs2(int u, int tp) {
    dfn[u] = ++tot, rk[tot] = u, top[u] = tp;
    if (son[u]) dfs2(son[u], tp);
    for (int i = hd[u]; i; i = nxt[i]) {
        if (to[i] != fa[u] && to[i] != son[u])
            dfs2(to[i], to[i]);
    }
}

void pushup(int o) {
    t[o].sum = t[t[o].ls].sum + t[t[o].rs].sum;
    t[o].mx = max(t[t[o].ls].mx, t[t[o].rs].mx);
}

void modify(int &o, int l, int r, int p) {
    if (!o) o = ++num;
    if (l == r) {
        t[o].sum = t[o].mx = w[rk[p]];
        return;
    }
    int mid = l + r >> 1;
    if (p <= mid) modify(t[o].ls, l, mid, p);
    else modify(t[o].rs, mid + 1, r, p);
    pushup(o);
}

void del(int o, int l, int r, int p) {
    if (l == r) {
        t[o].sum = t[o].mx = 0;
        return;
    }
    int mid = l + r >> 1;
    if (p <= mid) del(t[o].ls, l, mid, p);
    else del(t[o].rs, mid + 1, r, p);
    pushup(o);
}

int QuerySum(int o, int l, int r, int ql, int qr) {
    if (l >= ql && r <= qr) return t[o].sum;
    int mid = l + r >> 1, ret = 0;
    if (ql <= mid) ret += QuerySum(t[o].ls, l, mid, ql, qr);
    if (qr > mid) ret += QuerySum(t[o].rs, mid + 1, r, ql, qr);
    return ret;
}

int QueryMax(int o, int l, int r, int ql, int qr) {
    if (l >= ql && r <= qr) return t[o].mx;
    int mid = l + r >> 1, ret = 0;
    if (ql <= mid) chkmax(ret, QueryMax(t[o].ls, l, mid, ql, qr));
    if (qr > mid) chkmax(ret, QueryMax(t[o].rs, mid + 1, r, ql, qr));
    return ret;
}

int GetSum(int x, int y) {
    int tx = top[x], ty = top[y], ret = 0, cx = c[x], cy = c[y];
    while (tx != ty) {
        if (dep[tx] >= dep[ty])
            ret += QuerySum(rt[cx], 1, n, dfn[tx], dfn[x]), x = fa[tx];
        else
            ret += QuerySum(rt[cy], 1, n, dfn[ty], dfn[y]), y = fa[ty];
        tx = top[x], ty = top[y];
    }
    if (dfn[x] < dfn[y]) ret += QuerySum(rt[cx], 1, n, dfn[x], dfn[y]);
    else ret += QuerySum(rt[cx], 1, n, dfn[y], dfn[x]);
    return ret;
}

int GetMax(int x, int y) {
    int tx = top[x], ty = top[y], ret = 0, cx = c[x], cy = c[y];
    while (tx != ty) {
        if (dep[tx] >= dep[ty])
            chkmax(ret, QueryMax(rt[cx], 1, n, dfn[tx], dfn[x])), x = fa[tx];
        else
            chkmax(ret, QueryMax(rt[cy], 1, n, dfn[ty], dfn[y])), y = fa[ty];
        tx = top[x], ty = top[y];
    }
    if (dfn[x] < dfn[y]) chkmax(ret, QueryMax(rt[cx], 1, n, dfn[x], dfn[y]));
    else chkmax(ret, QueryMax(rt[cx], 1, n, dfn[y], dfn[x]));
    return ret;
}

int main() {
    int u, v;
    char op[5];
    n = gi(), q = gi();
    for (int i = 1; i <= n; ++i) w[i] = gi(), c[i] = gi();
    for (int i = 1; i < n; ++i) {
        u = gi(), v = gi();
        insert(u, v), insert(v, u);
    }
    dfs1(1, 0), dfs2(1, 1);
    for (int i = 1; i <= n; ++i) modify(rt[c[rk[i]]], 1, n, i);
    for (int i = 1; i <= q; ++i) {
        scanf("%s", op);
        u = gi(), v = gi();
        if (op[1] == 'C') {
            del(rt[c[u]], 1, n, dfn[u]);
            c[u] = v;
            modify(rt[v], 1, n, dfn[u]);
        }
        else if (op[1] == 'W') {
            w[u] = v;
            modify(rt[c[u]], 1, n, dfn[u]);
        }
        else if (op[1] == 'S') printf("%d\n", GetSum(u, v));
        else printf("%d\n", GetMax(u, v));
    }
    return 0;
}

以下是我因为 \(strcmp\) 导致的 MLE 而对着题解改的空间占用更少,加了一点卡常的代码

Code

LOJ上的码风真好看

猜你喜欢

转载自www.cnblogs.com/hlw1/p/12288614.html
今日推荐