[BZOJ3589]动态树 题解

[BZOJ3589]动态树 题解

题目地址:BZOJ:Problem 3589. — 动态树

题目描述

小明在楼下种了一棵动态树, 该树每天会在某些节点上长出一些果子. 这棵树的根节点为1, 它有n个节点, n-1条边.
别忘了这是一棵动态树, 每时每刻都是动态的. 小明要求你在这棵树上维护两种事件
事件0:
这棵树长出了一些果子, 即某个子树中的每个节点都会长出K个果子.
事件1:
小明希望你求出几条树枝上的果子数. 一条树枝其实就是一个从某个节点到根的路径的一段. 每次小明会选定一些树枝, 让你求出在这些树枝上的节点的果子数的和. 注意, 树枝之间可能会重合, 这时重合的部分的节点的果子只要算一次.
初始时, 每个节点上都没有果子.

输入输出格式

输入格式:
第一行一个整数n(1<=n<=200,000), 即节点数.
接下来n-1行, 每行两个数字u, v. 表示果子u和果子v之间有一条直接的边. 节点从1开始编号.
在接下来一个整数nQ(1<=nQ<=200,000), 表示事件.
最后nQ行, 每行开头要么是0, 要么是1.
如果是0, 表示这个事件是事件0. 这行接下来的2个整数u, delta表示以u为根的子树中的每个节点长出了delta个果子.
如果是1, 表示这个事件是事件1. 这行接下来一个整数K(1<=K<=5), 表示这次询问涉及K个树枝. 接下来K对整数u_k, v_k, 每个树枝从节点u_k到节点v_k. 由于果子数可能非常多, 请输出这个数模2^31的结果.

输出格式:
对于每个事件1, 输出询问的果子数.

输入输出样例

输入样例#1:

5
1 2
2 3
2 4
1 5
3
0 1 1
0 2 3
1 2 3 1 1 4

输出样例#1:

13

说明

1 <= n <= 200,000, 1 <= nQ <= 200,000, K = 5.
生成每个树枝的过程是这样的:先在树中随机找一个节点, 然后在这个节点到根的路径上随机选一个节点, 这两个节点就作为树枝的两端.

题解

又是树链信息,套树剖。考虑线段树维护区间和,然后使用打标记的形式来处理链并的问题。链上的点都打上标记,只有打标记的点的权值才会计入答案,这个线段树写是容易的。记得每次清空这次打过的标记。
至于那个取模,模数是2147483648,我们可以用int自然溢出以后对2147483647取与,这样就能把符号位搞掉,得到取模后的答案,减小常数。
吐槽:不要用memset处理很大的数组,会TLE。线段树做是O(\log n)比memsetO(n)优。

代码

// Code by KSkun, 2018/3
#include <cstdio>
#include <cstring>

#include <vector>

typedef long long LL;

inline char fgc() {
    static char buf[100000], *p1 = buf, *p2 = buf;
    return p1 == p2 && (p2 = (p1 = buf) + fread(buf, 1, 100000, stdin), p1 == p2) ? EOF : *p1++;
}

inline LL readint() {
    register LL res = 0, neg = 1;
    char c = fgc();
    while(c < '0' || c > '9') {
        if(c == '-') neg = -1;
        c = fgc();
    }
    while(c >= '0' && c <= '9') {
        res = res * 10 + c - '0';
        c = fgc();
    }
    return res * neg;
}

const int MAXN = 200005;

int n, q, ut, vt, op, xt, kt;
int fa[MAXN], siz[MAXN], son[MAXN], dfn[MAXN], ptn[MAXN], top[MAXN], dep[MAXN], cnt;
std::vector<int> gra[MAXN];

inline void addedge(int u, int v) {
    gra[u].push_back(v);
    gra[v].push_back(u);
}

inline void dfs1(int u) {
    for(int i = 0; i < gra[u].size(); i++) {
        int v = gra[u][i];
        if(v == fa[u]) continue;
        dep[v] = dep[u] + 1;
        fa[v] = u;
        dfs1(v);
        siz[u] += siz[v] + 1;
        if(siz[v] > siz[son[u]]) son[u] = v;
    }
}

inline void dfs2(int u, int tp) {
    top[u] = tp;
    dfn[u] = ++cnt;
    ptn[dfn[u]] = u;
    if(son[u]) dfs2(son[u], tp);
    for(int i = 0; i < gra[u].size(); i++) {
        int v = gra[u][i];
        if(v == son[u] || v == fa[u]) continue;
        dfs2(v, v);
    }
}

int val[MAXN << 2], sum[MAXN << 2], add[MAXN << 2], tag[MAXN << 2];

inline void pushdown(int o, int l, int r) {
    int mid = (l + r) >> 1, lch = o << 1, rch = (o << 1) | 1;
    if(add[o]) {
        add[lch] += add[o];
        add[rch] += add[o];
        val[lch] += add[o] * (mid - l + 1);
        val[rch] += add[o] * (r - mid);
        add[o] = 0;
    }
    if(tag[o] != -1) {
        tag[lch] = tag[rch] = tag[o];
        sum[lch] = val[lch] * tag[o];
        sum[rch] = val[rch] * tag[o];
        tag[o] = -1;
    }
}

inline void modify(int o, int l, int r, int ll, int rr, int v) {
    if(l >= ll && r <= rr) {
        val[o] += v * (r - l + 1);
        add[o] += v;
        return;
    }
    pushdown(o, l, r);
    int mid = (l + r) >> 1, lch = o << 1, rch = (o << 1) | 1;
    if(ll <= mid) modify(lch, l, mid, ll, rr, v);
    if(rr > mid) modify(rch, mid + 1, r, ll, rr, v);
    val[o] = val[lch] + val[rch];
}

inline void select(int o, int l, int r, int ll, int rr, int v) {
    if(l >= ll && r <= rr) {
        sum[o] = val[o] * v;
        tag[o] = v;
        return;
    }
    pushdown(o, l, r);
    int mid = (l + r) >> 1, lch = o << 1, rch = (o << 1) | 1;
    if(ll <= mid) select(lch, l, mid, ll, rr, v);
    if(rr > mid) select(rch, mid + 1, r, ll, rr, v);
    sum[o] = sum[lch] + sum[rch];
}

inline void modify(int u, int v, int x) {
    int tu = top[u], tv = top[v];
    while(tu != tv) {
        if(dep[tu] > dep[tv]) {
            std::swap(u, v);
            std::swap(tu, tv);
        }
        modify(1, 1, n, dfn[tv], dfn[v], x);
        v = fa[tv];
        tv = top[v];
    }
    if(dep[u] > dep[v]) std::swap(u, v);
    modify(1, 1, n, dfn[u], dfn[v], x);
}

inline void select(int u, int v) {
    int tu = top[u], tv = top[v];
    while(tu != tv) {
        if(dep[tu] > dep[tv]) {
            std::swap(u, v);
            std::swap(tu, tv);
        }
        select(1, 1, n, dfn[tv], dfn[v], 1);
        v = fa[tv];
        tv = top[v];
    }
    if(dep[u] > dep[v]) std::swap(u, v);
    select(1, 1, n, dfn[u], dfn[v], 1);
}

int main() {
    n = readint();
    for(int i = 1; i < n; i++) {
        ut = readint(); vt = readint();
        addedge(ut, vt);
    }
    dfs1(1);
    dfs2(1, 1);
    q = readint();
    while(q--) {
        op = readint();
        if(op == 0) {
            ut = readint(); xt = readint();
            modify(1, 1, n, dfn[ut], dfn[ut] + siz[ut], xt);
        } else {
            kt = readint();
            while(kt--) {
                ut = readint(); vt = readint();
                select(ut, vt);
            }
            printf("%d\n", sum[1] & 2147483647);
            select(1, 1, n, 1, n, 0);
        }
    }
    return 0;
}


发表评论

您的电子邮箱地址不会被公开。

此站点使用Akismet来减少垃圾评论。了解我们如何处理您的评论数据