BZOJ5379: Tree

链接

扯淡

据说这道题是CF几个月前某场比赛的题,然后被出到了前几天雅礼集训T1,于是就又成为了一道BZOJ题。
结果最后因为忘记建子文件夹光荣爆零
赛后测数据,第五个子任务在使用文件操作和不开O2的情况一个点1.5s左右轻松跑过所有测试点,更凉了。
真是雅礼集训最简单的题

Description

给定一棵树,树根初始为1,每个点有权值,有三种操作:
1.将根设为x;
2.将lca(x, y)的子树内所有点的权值加上z;
3.查询x的子树内所有点的权值和。

Input

第一行两个整数n, q,表示树的大小和操作个数。
接下来一行n个整数,为每个点的权值。
接下来n-1行,每行两个整数x, y,表示一条树边。
接下来q行,每行若干个数,表示一次操作。

Output

对于每个3操作,输出一行一个整数表示答案。

Solution

如果没有1操作,那么这就是树剖裸题。那么我们就要思考1操作对求lca和子树问题的影响。
我才不会跟你们说我在考场上脑子里想的全是遥远的国度
事实上,我没做过那个题。
那么自己yy的时候就到了。
先对最开始那颗根为1的树(称为原树)进行树剖,可以发现:
当我们要换的根不在原树中lca(x, y)的子树内时,换根对求lca和修改/查询子树都没有影响;
当换的根在lca(x, y)的子树内时,我们要求的lca(x, y)就变成了原树中lca(x, root)与lca(y, root)中深度更深的一个
对于修改/询问子树,假设我们想得到x的子树,设y为与x相邻的且距离root最近的点(即从x到root最短路上经过的第一个点),此时新树中x的子树就是除原树中y的子树之外的所有点。那么怎么求y呢?将root向上倍增到x的深度+1的点,就是y了。

Code

#include <iostream>
#include <cstring>
#include <cstdio>

const int maxn = 3e5 + 7;

typedef long long ll;

using namespace std;

int to[maxn << 1];
int nex[maxn << 1];
int last[maxn], k;

int root;
int w[maxn];
int id[maxn], cnt;
int fa[maxn];
int son[maxn];
int dep[maxn];
int top[maxn];
int val[maxn];
int size[maxn];
int rmq[maxn][18];

ll st[maxn << 2];
ll len[maxn << 2];
ll add[maxn << 2];

inline int read()
{
    int X = 0, w = 1; char ch = getchar();
    while (ch < '0' || ch > '9') { if (ch == '-') w = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9') X = X * 10 + ch - '0', ch = getchar();
    return X * w;
}

inline void add_edge(int x, int y)
{
    to[++k] = y; nex[k] = last[x]; last[x] = k;
}

inline void pushup(int rt)
{
    st[rt] = st[rt << 1] + st[rt << 1 | 1];
}

inline void pushdown(int rt)
{
    if (add[rt]) {
        int ls = rt << 1, rs = ls | 1;
        add[ls] += add[rt];
        add[rs] += add[rt];
        st[ls] += len[ls] * add[rt];
        st[rs] += len[rs] * add[rt];
        add[rt] = 0;
    }
}

void build(int l, int r, int rt)
{
    len[rt] = r - l + 1;
    if (l == r) {
        st[rt] = val[l];
        return;
    }
    int mid = l + r >> 1;
    build(l, mid, rt << 1);
    build(mid + 1, r, rt << 1 | 1);
    pushup(rt);
}

void update(int x, int y, int z, int l, int r, int rt)
{
    if (l >= x && r <= y) {
        st[rt] += len[rt] * z;
        add[rt] += z;
        return;
    }
    pushdown(rt);
    int mid = l + r >> 1;
    if (x <= mid) update(x, y, z, l, mid, rt << 1);
    if (y > mid) update(x, y, z, mid + 1, r, rt << 1 | 1);
    pushup(rt);
}

ll query(int x, int y, int l, int r, int rt)
{
    if (l >= x && r <= y) return st[rt];
    pushdown(rt);
    int mid = l + r >> 1;
    ll ans = 0;
    if (x <= mid) ans += query(x, y, l, mid, rt << 1);
    if (y > mid) ans += query(x, y, mid + 1, r, rt << 1 | 1);
    return ans;
}

void dfs1(int x, int f, int depth)
{
    dep[x] = depth;
    fa[x] = f;
    size[x] = 1;
    int wson = -1;
    for (int i = last[x]; i; i = nex[i]) {
        int y = to[i];
        if (y == f) continue;
        dfs1(y, x, depth + 1);
        size[x] += size[y];
        if (size[y] > wson) wson = size[y], son[x] = y;
    }
}

void dfs2(int x, int topf)
{
    id[x] = ++cnt;
    val[cnt] = w[x];
    top[x] = topf;
    if (!son[x]) return;
    dfs2(son[x], topf);
    for (int i = last[x]; i; i = nex[i]) {
        int y = to[i];
        if (y == fa[x] || y == son[x]) continue;
        dfs2(y, y);
    }
}

inline int lca(int x, int y)
{
    while (top[x] != top[y]) {
        if (dep[top[x]] < dep[top[y]]) y = fa[top[y]];
        else x = fa[top[x]];
    }
    if (dep[x] > dep[y]) return y;
    return x;
}

void dfs(int x)
{
    rmq[x][0] = fa[x];
    for (int i = 1; dep[x] >> i; i++)
        rmq[x][i] = rmq[rmq[x][i - 1]][i - 1];
    for (int i = last[x]; i; i = nex[i])
        if (to[i] != fa[x]) dfs(to[i]);
}

inline int qnext(int x, int rt)
{
    int depth = dep[rt] - dep[x] - 1;
    for (int i = 0; i <= 17; i++)
        if (depth & (1 << i)) rt = rmq[rt][i];
    return rt;
}

int main(void)
{
    register int n = read(), q = read();
    for (int i = 1; i <= n; i++) w[i] = read();
    for (int i = 1; i < n; i++) {
        int x = read(), y = read();
        add_edge(x, y);
        add_edge(y, x);
    }
    root = 1;
    dfs1(1, 0, 1);
    dfs2(1, 1);
    dfs(1);
    build(1, n, 1);
    while (q--) {
        int opt = read(), x = read();
        if (opt == 1) root = x;
        else if (opt == 2) {
            int y = read(), z = read();
            int lca1 = lca(x, root), lca2 = lca(y, root), p;
            if (lca1 == lca2) p = lca(x, y);
            //当lca(x, root) = lca(y, root) = root即root在lca(x, y)子树之外时,没有影响;
            else p = dep[lca1] > dep[lca2] ? lca1 : lca2;
            //不然就是lca(x, root)与lca(y, root)中深度更深的一个
            if (p == root) update(1, n, z, 1, n, 1);
            //当lca(x, y) = root时,子树是整棵树;
            else if (id[root] < id[p] || id[root] >= id[p] + size[p])
                update(id[p], id[p] + size[p] - 1, z, 1, n, 1); //当root在lca(x, y)的子树之外时,没有影响,直接查询;
            else {
                y = qnext(p, root);
                st[1] += n * z;
                add[1] += z;
                update(id[y], id[y] + size[y] - 1, -z, 1, n, 1);
                //否则修改y的子树以外的值,y的含义见上述内容。
            }
        }
        else {
            if (x == root) printf("%lld\n", st[1]);
            else if (lca(root, x) != x) printf("%lld\n", query(id[x], id[x] + size[x] - 1, 1, n, 1));
            else {
                int y = qnext(x, root);
                printf("%lld\n", st[1] - query(id[y], id[y] + size[y] - 1, 1, n, 1));
            }
            //查询同修改
        }
    }

    return 0;
}

猜你喜欢

转载自blog.csdn.net/star_city_7/article/details/80787051