zoukankan      html  css  js  c++  java
  • [WC 2013]糖果公园

    Description

    题库链接

    给你一棵 (n) 个节点,有 (m) 种颜色的树。每个节点上有一个颜色。定义一条树上路径的价值为

    [sum_c V_c(sum_{i=1}^{tim_c}W_i)]

    其中 (V,W) 已经给出, (tim_c) 表示路径上 (c) 颜色的节点数。

    现在给出 (q) 个操作,让你实现:

    1. 修改节点颜色;
    2. 询问树上路径的价值。

    (1leq n,m,qleq 100000)

    Solution

    如果这题出在序列上而不是在树上,很容易用莫队求解。

    考虑用类似的方法,我们将树分块,采用[SCOI 2005]王室联邦的方法。

    对于树上分块的复杂度和块的大小的选取可以参见ljh的博客,这里摘出了一些:

    (block_{num}) 为块数, (block_{size}) 为块的大小,则有 (block_{num} imes block_{size}=n) ,在证明中我们假设 (n,q) 同阶。
    设块对 ((block_i,block_j)) ,易知这样的块对不会超过 (block_{size}^2) 个。
    对于块对内的操作:我们考虑总复杂度,左端点共移动至多 (O(q imes block_{size})) ,右端点亦是。时间共移动至多 (O(block_{num}^2 imes q)) 。故这一部分的复杂度为 (O(n imes(block_{size}+block_{num}^2)))
    对于块与块之间的操作,不超过 (block_{num}^2) 次:左端第移动一次,最多 (O(n)) ,右端点亦是如此。时间最多移动 (O(q)=O(n)) 。故这一部分复杂度为 (O(block_{num}^2 imes n))
    故总复杂度为 (O(n imes(block_{size}+block_{num}^2)))
    可以证明当 (block_{size}=n^{frac{2}{3}}) 时, (block_{num}=n^{frac{1}{3}}) ,复杂度最优,为 (O(n^{frac{5}{3}}))

    至于莫队的操作,就是将序列中移动左右端点变成在树上移动路径的两个端点。对于修改,我们同样是模拟时间倒流和消逝。

    那么怎么去移动结点来保证提取出路径?

    考虑我们树上的所有结点,实际上可以认为是 (0/1) 状态——计入答案或者未计入答案。

    考虑用类似于异或的思想来执行操作,比如:计入答案再从答案中去掉,等于异或了两次 (1) ,就等于原来的数。假设这次的起点、终点为 (u,v) ,上次为 (x,y) ,那么可以对 (x)(u) 的路径、 (v)(y) 的路径进行一次取 (xor) 操作。注意的是对 (lca) 不做处理,这样就能保证每次操作之后图上打上标记的点只在 ((u,lca))((v,lca)) 的路径上(不包括 (lca) )。

    Code

    #include <bits/stdc++.h>
    using namespace std;
    const int N = 1e5;
    
    int n, m, q, v[N+5], w[N+5], c[N+5], tq, tc, s[N+5], tol, pre[N+5], tim[N+5];
    int dfn[N+5], times, size[N+5], son[N+5], tp[N+5], fa[N+5], dep[N+5], rev[N+5];
    long long ans[N+5], sum;
    struct tt {int to, next; }edge[(N<<1)+5];
    int path[N+5], top, blocks[N+5], block_size, cnt;
    struct operation {
        int l, r, p, id;
        bool operator < (const operation &b) const {
        if (blocks[l] != blocks[b.l]) return blocks[l] < blocks[b.l];
        if (blocks[r] != blocks[b.r]) return blocks[r] < blocks[b.r];
        return id < b.id;
        }
    }qry[N+5], ch[N+5];
    
    void add(int u, int v) {edge[++top] = (tt){v, path[u]}, path[u] = top; }
    void dfs1(int u, int father, int depth) {
        int bot = tol;
        dfn[u] = ++times, size[u] = 1, dep[u] = depth, fa[u] = father;
        for (int i = path[u], v; i; i = edge[i].next)
        if ((v = edge[i].to) != father) {
            dfs1(v, u, depth+1);
            size[u] += size[v];
            if (size[v] > size[son[u]]) son[u] = v;
            if (tol-bot >= block_size) {
            ++cnt;
            while (tol != bot) blocks[s[tol--]] = cnt;
            }
        } 
        s[++tol] = u;
    }
    void dfs2(int u, int top) {
        tp[u] = top;
        if (son[u]) dfs2(son[u], top);
        for (int i = path[u]; i; i = edge[i].next)
        if (edge[i].to != son[u] && edge[i].to != fa[u])
            dfs2(edge[i].to, edge[i].to);
    }
    int get_lca(int u, int v) {
        while (tp[u] != tp[v]) {
        if (dep[tp[u]] < dep[tp[v]]) swap(u, v);
        u = fa[tp[u]];
        }
        return dep[u] < dep[v] ? u : v;
    }
    void reverse(int x) {
        if (rev[x]) sum -= 1ll*v[c[x]]*w[tim[c[x]]], --tim[c[x]];
        else ++tim[c[x]], sum += 1ll*v[c[x]]*w[tim[c[x]]];
        rev[x] ^= 1;
    }
    void upd(int x, int y) {
        if (rev[x]) reverse(x), c[x] = y, reverse(x);
        else c[x] = y;
    }
    void move(int x, int y) {
        while (x != y) {
        if (dep[x] < dep[y]) swap(x, y);
        reverse(x); x = fa[x];
        }
    }
    void work() {
        scanf("%d%d%d", &n, &m, &q); block_size = pow(n, 0.6667);
        for (int i = 1; i <= m; i++) scanf("%d", &v[i]);
        for (int i = 1; i <= n; i++) scanf("%d", &w[i]);
        for (int i = 1, u, v; i < n; i++) {
        scanf("%d%d", &u, &v); add(u, v), add(v, u);
        }
        dfs1(1, 0, 1); dfs2(1, 1);
        while (tol) blocks[s[tol--]] = cnt;
        for (int i = 1; i <= n; i++) scanf("%d", &c[i]), pre[i] = c[i];
        for (int i = 1, opt, x, y; i <= q; i++) {
        scanf("%d%d%d", &opt, &x, &y);
        if (opt == 0) ch[++tc] = (operation){x, y, pre[x], 0}, pre[x] = y;
        else {
            if (dfn[x] > dfn[y]) swap(x, y);
            qry[++tq] = (operation){x, y, tc, tq};
        }
        }
        sort(qry+1, qry+tq+1);
        int curt = 0, curl = 1, curr = 1;
        for (int i = 1; i <= tq; i++) {
        while (curt < qry[i].p) ++curt, upd(ch[curt].l, ch[curt].r);
        while (curt > qry[i].p) upd(ch[curt].l, ch[curt].p), --curt;
        move(curl, qry[i].l); curl = qry[i].l;
        move(curr, qry[i].r); curr = qry[i].r;
        int lca = get_lca(curl, curr);
        reverse(lca);
        ans[qry[i].id] = sum;
        reverse(lca);
        }
        for (int i = 1; i <= tq; i++) printf("%lld
    ", ans[i]);
    }
    int main() {work(); return 0; }
  • 相关阅读:
    (转)在WPF中自定义控件 CustomControl (下)注意TemplatePartAttribute
    [STAThread]的含义
    Exception of Storyboard in controlTemplate,can't use binding or dynamic resource
    What is the difference between CollectionView and CollectionViewSource?
    EssentialWPF_chapter6_Data
    WPF 调试方法, WPF Debug
    System.Windows.Markup.ContentPropertyAttribute
    Layout相关
    When use registerReadonly
    注意:匿名事件处理函数
  • 原文地址:https://www.cnblogs.com/NaVi-Awson/p/8761502.html
Copyright © 2011-2022 走看看