「题解」洛谷 P4074 [WC2013]糖果公园

题目

P4074 [WC2013]糖果公园

简化题意

给你一棵树树,点有点权,带修改,每一次经过一种点权会有不同的贡献(随着经过次数再变),问你从一个点到一个点的贡献和

思路

树上带修莫队。

(cnt[i]) 表示 (i) 这个点的权值的出现次数。

(a[i]) 表示 (i) 号点的权值。

(v[i]) 表示一权值 (i) 的贡献定值。

(v[i]) 表示一权值出现 (i) 次时的贡献常数。

加入一个点 (u)ans += v[++cnt[a[u]]] * w[a[u]]

删掉一个点 (u)ans -= v[cnt[a[u]]--] * w[a[u]]

先考虑没有修改,因为加一个点和删一个点都可以 (O(1)) 维护,所以莫队没问题。

在树上,树上莫队。

如果没有修改就是一个普通的树上莫队。有修改就是树上带修莫队了。

Code

#include <cmath>
#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <algorithm>
#define MAXN 100001

inline void read(int &T) {
    int x = 0;
    bool f = 0;
    char c = getchar();
    while (c < '0' || c > '9') {
        if (c == '-') f = !f;
        c = getchar();
    }
    while (c >= '0' && c <= '9') {
        x = x * 10 + c - '0';
        c = getchar();
    }
    T = f ? -x : x;
}

void write(long long x) {
    if (x < 0) putchar('-'), write(-x);
    else {
        if (x / 10) write(x / 10);
        putchar(x % 10 + '0');
    }
}

typedef long long ll;
ll ans[MAXN];
int n, m, s, sqrn, pthn, head[MAXN], a[MAXN], v[MAXN], w[MAXN], num[MAXN << 1], cnt[MAXN], vis[MAXN];
int c_, cc, cq, fir[MAXN], la[MAXN], pre[MAXN << 1], dep[MAXN], lg[MAXN], fa[MAXN][21];
struct change {
    int w, pos;
}c[MAXN];
struct query {
    int x, y, t, lca, id;
    friend bool operator < (query q1, query q2) {
        if (num[q1.x] != num[q2.x]) return num[q1.x] < num[q2.x];
        if (num[q1.y] != num[q2.y]) return num[q1.y] < num[q2.y];
        return q1.t < q2.t;
    }
}q[MAXN];
struct Edge {
    int next ,to;
}pth[MAXN << 1];

void add(int from, int to) {
    pth[++pthn].to = to, pth[pthn].next = head[from];
    head[from] = pthn;
}

void dfs(int u, int father) {
    fa[u][0] = father, dep[u] = dep[father] + 1;
    fir[u] = ++c_, pre[c_] = u;
    for (int i = head[u]; i; i = pth[i].next) {
        int x = pth[i].to;
        if (x != father) dfs(x, u);
    }
    la[u] = ++c_, pre[c_] = u;
}

int lca(int x, int y) {
    if (dep[x] < dep[y]) std::swap(x, y);
    while (dep[x] > dep[y]) {
        x = fa[x][lg[dep[x] - dep[y]] - 1];
    }
    if (x == y) return x;
    for (int k = lg[dep[x]] - 1; k >= 0; --k) {
        if (fa[x][k] != fa[y][k]) {
            x = fa[x][k];
            y = fa[y][k];
        }
    }
    return fa[x][0];
}

void work(int now, ll &ans) {
    if (vis[now]) {
        ans -= 1ll * w[cnt[a[now]]] * v[a[now]];
        --cnt[a[now]];
    }
    else {
        ++cnt[a[now]];
        ans += 1ll * w[cnt[a[now]]] * v[a[now]];
    }
    vis[now] ^= 1;
}

bool okay(int nq, int node) {
    bool flag1 = (fir[node] >= q[nq].x && fir[node] <= q[nq].y && (la[node] < q[nq].x || la[node] > q[nq].y));
    bool flag2 = (la[node] >= q[nq].x && la[node] <= q[nq].y && (fir[node] < q[nq].x || fir[node] > q[nq].y));
    return (flag1 || flag2);
}

int main() {
    read(n), read(m), read(s), sqrn = pow(2 * n, 2.0 / 3.0);
    for (int i = 1; i <= 2 * n; ++i) num[i] = (i - 1) / sqrn + 1;
    for (int i = 1; i <= m; ++i) read(v[i]);
    for (int i = 1; i <= n; ++i) read(w[i]);
    for (int i = 1, u, v; i < n; ++i) {
        read(u), read(v);
        add(u, v), add(v, u);
    }
    dfs(1, 0);
    for (int i = 1; i <= n; ++i) {
        lg[i] = lg[i - 1] + ((1 << lg[i - 1]) == i);
    }
    for (int j = 1; (1 << j) <= n; ++j) {
        for (int i = 1; i <= n; ++i) {
            fa[i][j] = fa[fa[i][j - 1]][j - 1];
        }
    }
    for (int i = 1; i <= n; ++i) read(a[i]);
    for (int i = 1, opt, x, y, f; i <= s; ++i) {
        read(opt), read(x), read(y);
        if (opt == 1) {
            f = lca(x, y), q[++cq].id = cq, q[cq].t = cc;
            if (fir[x] > fir[y]) std::swap(x, y);
            if (x == f) {
                q[cq].x = fir[x];
                q[cq].y = fir[y];
            }
            else {
                q[cq].x = la[x];
                q[cq].y = fir[y];
                q[cq].lca = f;
            }
        }
        else c[++cc].w = y, c[cc].pos = x;
    }
    std::sort(q + 1, q + cq + 1);
    int l = 1, r = 0, t = 0;
    ll now = 0;
    for (int i = 1; i <= cq; ++i) {
        while (l > q[i].x) work(pre[--l], now);
        while (r < q[i].y) work(pre[++r], now);
        while (l < q[i].x) work(pre[l++], now);
        while (r > q[i].y) work(pre[r--], now);
        while (t < q[i].t) {
            ++t;
            bool flag = okay(i, c[t].pos);
            if (flag) work(c[t].pos, now);
            std::swap(a[c[t].pos], c[t].w);
            if (flag) work(c[t].pos, now);
        }
        while (t > q[i].t) {
            bool flag = okay(i, c[t].pos);
            if (flag) work(c[t].pos, now);
            std::swap(a[c[t].pos], c[t].w);
            if (flag) work(c[t].pos, now);
            --t;
        }
        if (q[i].lca) work(q[i].lca, now);
        ans[q[i].id] = now;
        if (q[i].lca) work(q[i].lca, now);
    }
    for (int i = 1; i <= cq; ++i) {
        write(ans[i]);
        puts("");
    }
    return 0;
}
原文地址:https://www.cnblogs.com/poi-bolg-poi/p/13594940.html