#58. 【WC2013】糖果公园

题目链接
Candyland 有一座糖果公园,公园里不仅有美丽的风景、好玩的游乐项目,还有许多免费糖果的发放点,这引来了许多贪吃的小朋友来糖果公园玩。

糖果公园的结构十分奇特,它由 n n n 个游览点构成,每个游览点都有一个糖果发放处,我们可以依次将游览点编号为 1 1 1 n n n。有 n − 1 n−1 n1 条双向道路连接着这些游览点,并且整个糖果公园都是连通的,即从任何一个游览点出发都可以通过这些道路到达公园里的所有其它游览点。

糖果公园所发放的糖果种类非常丰富,总共 m m m 种,它们的编号依次为 1 1 1 m m m。每一个糖果发放处都只发放某种特定的糖果,我们用 c i c_i ci 来表示 i i i 号游览点的糖果。

来到公园里游玩的游客都不喜欢走回头路,他们总是从某个特定的游览点出发前往另一个特定的游览点,并游览途中的景点,这条路线一定是唯一的。他们经过每个游览点,都可以品尝到一颗对应种类的糖果。

大家对不同类型的糖果的喜爱程度都不尽相同。根据游客们的反馈打分,我们得到了糖果的美味指数,第 i i i 种糖果的美味指数为 v i v_i vi。另外,如果一位游客反复地品尝同一种类的糖果,他肯定会觉得有一些腻。根据量化统计,我们得到了游客第 i i i 次品尝某类糖果的新奇指数 w i w_i wi,如果一位游客第 i i i 次品尝第 j j j 种糖果,那么他的愉悦指数 H H H 将会增加对应的美味指数与新奇指数的乘积,即 v j w i v_jw_i vjwi。这位游客游览公园的愉悦指数最终将是这些乘积的和。

当然,公园中每个糖果发放点所发放的糖果种类不一定是一成不变的。有时,一些糖果点所发放的糖果种类可能会更改(也只会是 m m m 种中的一种),这样的目的是能够让游客们总是感受到惊喜。

糖果公园的工作人员小 A 接到了一个任务,那就是根据公园最近的数据统计出每位游客游玩公园的愉悦指数。但数学不好的小 A 一看到密密麻麻的数字就觉得头晕,作为小 A 最好的朋友,你决定帮他一把。

输入格式
第一行包含三个正整数 n , m , q n,m,q n,m,q,分别表示游览点个数、糖果种类数和操作次数。

第二行包含 m m m 个正整数 v 1 , v 2 , … , v m v_1,v_2,…,v_m v1,v2,,vm

第三行包含 n n n 个正整数 w 1 , w 2 , … , w n w_1,w_2,…,w_n w1,w2,,wn

第四行到第 n + 2 n+2 n+2 行,每行包含两个正整数 a i , b i a_i,b_i ai,bi,表示这两个游览点之间有路径可以直接到达。

n + 3 n+3 n+3 行包含 n n n 个正整数 c 1 , c 2 , … , c n c_1,c_2,…,c_n c1,c2,,cn

接下来 q q q 行,每行包含三个整数 t , x , y t,x,y t,x,y,表示一次操作:

t t t 0 0 0,则 1 ≤ x ≤ n 1≤x≤n 1xn 1 ≤ y ≤ m 1≤y≤m 1ym,表示编号为 x x x 的游览点发放的糖果类型改为 y y y

t t t 1 1 1,则 1 ≤ x , y ≤ n 1≤x,y≤n 1x,yn,表示对出发点为 x x x,终止点为 y y y 的路线询问愉悦指数。

输出格式
按照输入的先后顺序,对于每个 t t t 1 1 1 的操作输出一行,用一个正整数表示答案。

样例一
input

4 3 5
1 9 2
7 6 5 1
2 3
3 1
3 4
1 2 3 2
1 1 2
1 4 2
0 2 1
1 1 2
1 4 2

output

84
131
27
84

限制与约定
对于所有的数据, 1 ≤ v i , w i ≤ 1 0 6 1≤v_i,w_i≤10^6 1vi,wi106 1 ≤ a i , b i ≤ n 1≤a_i,b_i≤n 1ai,bin 1 ≤ c i ≤ m 1≤c_i≤m 1cim w 1 , w 2 , … , w n w_1,w_2,…,w_n w1,w2,,wn 是非递增序列,即对任意 1 < i ≤ n 1<i≤n 1<in,满足 w i ≤ w i − 1 w_i≤w_i−1 wiwi1

树上莫队的其中一种方法是在树的括号序上进行莫队,这种方法适合对路径上的询问进行求解。

括号序即在 d f s dfs dfs时每一个第一次访问和回溯形成的序列,每一个节点都在序列中出现两次,设节点 i i i两次出现的位置为分别为 i n [ i ] in[i] in[i] o u t [ i ] out[i] out[i]

对于一次询问 [ l , r ] [l,r] [l,r],假设 d f n [ l ] < d f n [ r ] dfn[l]<dfn[r] dfn[l]<dfn[r](如果不满足则交换 l , r l,r l,r的值),则如果 r r r l l l的子树中,则在区间 [ i n [ l ] , i n [ r ] ] [in[l],in[r]] [in[l],in[r]]中只出现过一次的点中求解结果,因为出现两次说明该节点进行了完整的递归和回溯,因此不在路径上;如果 r r r不在 l l l的子树中,则在 [ o u t [ r ] , i n [ l ] ] [out[r],in[l]] [out[r],in[l]]中只出现过一次的点和 l c a ( l , r ) lca(l,r) lca(l,r)中求解结果,因为 l l l r r r l c a ( x , y ) lca(x,y) lca(x,y)的两个子树中,可以转化为第一种情况,但 [ o u t [ r ] , i n [ l ] ] [out[r],in[l]] [out[r],in[l]]没有计算 l c a ( x , y ) lca(x,y) lca(x,y)的贡献,因此要额外加上。

#include<bits/stdc++.h>

#define ll long long
using namespace std;

inline int qr() {
    int f = 0, fu = 1;
    char c = getchar();
    while (c < '0' || c > '9') {
        if (c == '-')fu = -1;
        c = getchar();
    }
    while (c >= '0' && c <= '9') {
        f = (f << 3) + (f << 1) + c - 48;
        c = getchar();
    }
    return f * fu;
}

const int N = 1e5 + 10;

struct MODUI {
    int head[N], ver[N << 1], Next[N << 1], tot;
    int d[N], f[N], s[N], son[N], dfn[N], top[N], num;

    struct Q {
        int l, r, t, x, y, i, o;

        inline Q() {}

        inline Q(int l, int r, int t, int i, int o, int len) : l(l), r(r), t(t), i(i), o(o) {
            x = l / len, y = r / len;
        }

        inline friend bool operator<(const Q &a, const Q &b) {
            return a.x == b.x ? a.y == b.y ? (a.y & 1) ? a.t < b.t : a.t > b.t : a.y < b.y : a.x < b.x;
        }
    } q[N];

    inline void add(int x, int y) {
        ver[++tot] = y;
        Next[tot] = head[x];
        head[x] = tot;
    }

    void dfs0(int x) {
        s[x] = 1;
        for (int i = head[x]; i; i = Next[i]) {
            int y = ver[i];
            if (y == f[x])continue;
            f[y] = x, d[y] = d[x] + 1;
            dfs0(y), s[x] += s[y];
            if (s[y] > s[son[x]])son[x] = y;
        }
    }

    void dfs1(int x, int p) {
        dfn[x] = ++num, top[x] = p;
        if (son[x])dfs1(son[x], p);
        for (int i = head[x]; i; i = Next[i]) {
            int y = ver[i];
            if (y == f[x] || y == son[x])continue;
            dfs1(y, y);
        }
    }

    inline int lca(int x, int y) {
        while (top[x] != top[y]) {
            if (d[top[x]] < d[top[y]])swap(x, y);
            x = f[top[x]];
        }
        return d[x] < d[y] ? x : y;
    }

    ll v[N], w[N], ans[N], now;
    int c[N], in[N], out[N], p[N << 1], tag[N << 1], cnt[N];
    int tx[N], ty[N], k;

    void dfs2(int x) {
        in[x] = ++num, p[num] = x;
        if (son[x])dfs2(son[x]);
        for (int i = head[x]; i; i = Next[i]) {
            int y = ver[i];
            if (y == f[x] || y == son[x])continue;
            dfs2(y);
        }
        out[x] = ++num, p[num] = x;
    }

    inline void work(int x) {
        if (tag[x])now -= v[c[x]] * w[cnt[c[x]]--], tag[x] = 0;
        else now += v[c[x]] * w[++cnt[c[x]]], tag[x] = 1;
    }

    inline void update(int i, int x) {
        if (c[tx[x]] == ty[x])return;
        bool r1 = q[i].l <= in[tx[x]] && in[tx[x]] <= q[i].r;
        bool r2 = q[i].l <= out[tx[x]] && out[tx[x]] <= q[i].r;
        if (!(r1 ^ r2))return swap(c[tx[x]], ty[x]), void();
        work(tx[x]), swap(c[tx[x]], ty[x]), work(tx[x]);
    }

    inline void solve() {
        int n = qr(), m = qr(), cq = qr();
        int len = max(1, int(pow(n, 2.0 / 3.0)));
        for (int i = 1; i <= m; i++)v[i] = qr();
        for (int i = 1; i <= n; i++)w[i] = qr();
        for (int i = 1, x, y; i <= n - 1; i++)
            x = qr(), y = qr(), add(x, y), add(y, x);
        for (int i = 1; i <= n; i++)c[i] = qr();
        d[1] = 1, dfs0(1), dfs1(1, 1);
        num = 0, dfs2(1);
        for (int i = 1, o, x, y, t = 0; i <= cq; i++) {
            o = qr(), x = qr(), y = qr();
            if (o) {
                ++k;
                if (dfn[x] > dfn[y])swap(x, y);
                if (dfn[y] < dfn[x] + s[x])q[k] = Q(in[x], in[y], t, k, 0, len);
                else q[k] = Q(out[x], in[y], t, k, 1, len);
            } else tx[++t] = x, ty[t] = y;
        }
        sort(q + 1, q + 1 + k);
        for (int i = 1, l = 1, r = 0, t = 0; i <= k; i++) {
            while (l > q[i].l)work(p[--l]);
            while (r < q[i].r)work(p[++r]);
            while (l < q[i].l) work(p[l++]);
            while (r > q[i].r) work(p[r--]);
            while (t < q[i].t) update(i, ++t);
            while (t > q[i].t) update(i, t--);
            if (q[i].o) work(lca(p[l], p[r]));
            ans[q[i].i] = now;
            if (q[i].o) work(lca(p[l], p[r]));
        }
        for (int i = 1; i <= k; i++)printf("%lld\n", ans[i]);
    }
} M;

int main() {
    M.solve();
    return 0;
}
©️2020 CSDN 皮肤主题: 数字20 设计师:CSDN官方博客 返回首页