ACM-ICPC 2018 焦作赛区网络预赛 E 树链剖分

题目链接


题意:给定一棵以 1 1 1为根的节点,存在路径加,路径乘,路径的节点权值取反,查询路径和等四个操作。


思路:

典型的树链剖分题目。

对于取反,可以转化成减法操作,比如:
00011取反为:11100
等价于 (11111 - 00011) = 11100
即将原值乘上(-1),再加上 2 l e n − 1 2^{len} - 1 2len1 l e n len len为二进制的位数

这样就可以把取反操作转化为加法和乘法操作。

对于路径加和乘法的维护,可以使用两个变量add, mul。
则 线段树的节点值为: s u m ∗ m u l + a d d sum * mul + add summul+add


更新为 + c +c +c时,则 a d d + c add + c add+c
更新为 ∗ c *c c时,则$mul*c $且 a d d ∗ c add * c addc

此题得解。


代码:

#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cstdlib>
#include<iostream>
using namespace std;
typedef unsigned long long ull;
#define lson rt<<1
#define rson rt<<1|1

const int A = 2e5 + 10;
class Gra{
public:
    int v,next;
}G[A<<2];

class Seg_Tree{
public:
    int l,r,len;
    ull sum, add, mul;
}Tree[A<<2];

int n, m, head[A], tot, twt;
int fa[A], dep[A], siz[A], son[A], top[A], pos[A], ID[A];
ull Val;

void Init(){
    for (int i = 0; i < A; i++) head[i] = -1;
    tot = twt = 0;
}

void add(int u,int v){
    G[tot].v = v;
    G[tot].next = head[u];
    head[u] = tot++;
}

void dfs(int u, int pre, int d){
    fa[u] = pre;son[u] = -1;
    siz[u] = 1;dep[u] = d;

    for (int i = head[u]; i != -1; i=G[i].next) {
        int v = G[i].v;
        if (v == pre) continue;
        dfs(v, u, d + 1);
        siz[u] += siz[v];
        if (son[u] == -1 || siz[v] > siz[son[u]]) son[u] = v;
    }
}

void dfs(int u, int tp){
    pos[u] = ++twt;
    ID[twt] = u;
    top[u] = tp;

    if (son[u] == -1) return;
    dfs(son[u], tp);

    for (int i = head[u]; i != -1; i = G[i].next) {
        int v = G[i].v;
        if(v == son[u] || v == fa[u]) continue;
        dfs(v,v);
    }
}

void push_up(int rt){
    Tree[rt].sum = Tree[lson].sum + Tree[rson].sum;
}

void push_down(int rt){
    if (Tree[rt].mul != 1) {
        Tree[lson].mul *= Tree[rt].mul;
        Tree[rson].mul *= Tree[rt].mul;
        Tree[lson].add *= Tree[rt].mul;
        Tree[rson].add *= Tree[rt].mul;
        Tree[lson].sum *= Tree[rt].mul;
        Tree[rson].sum *= Tree[rt].mul;
        Tree[rt].mul = 1;
    }
    if (Tree[rt].add) {
        Tree[lson].add += Tree[rt].add;
        Tree[rson].add += Tree[rt].add;
        Tree[lson].sum += Tree[lson].len * Tree[rt].add;
        Tree[rson].sum += Tree[rson].len * Tree[rt].add;
        Tree[rt].add = 0;
    }
}

void build_Tree(int rt,ull l,ull r){
    Tree[rt].l = l;Tree[rt].r = r;
    Tree[rt].len = r - l + 1;
    Tree[rt].sum = Tree[rt].add = 0;
    Tree[rt].mul = 1;

    if (l == r) return;
    ull mid = (l+r)/2;
    build_Tree(lson, l, mid);
    build_Tree(rson, mid + 1, r);

    push_up(rt);
}

void update_Tree(int rt, ull st, ull ed, ull c, int id){
    ull l = Tree[rt].l, r = Tree[rt].r;
    if (st <= l && r <= ed) {
        push_down(rt);
        if (id) {
            Tree[rt].sum += Tree[rt].len * c;
            Tree[rt].add += c;
        } else {
            Tree[rt].sum *= c;
            Tree[rt].add *= c;
            Tree[rt].mul *= c;
        }
        return;
    }
    push_down(rt);
    ull mid = (l + r)/2;
    if (st <= mid) update_Tree(lson, st, ed, c, id);
    if (ed >  mid) update_Tree(rson, st, ed, c, id);
    push_up(rt);
}

ull query_Tree(int rt, ull st, ull ed){
    ull l = Tree[rt].l, r = Tree[rt].r;
    if (st <= l && r <= ed) {
        push_down(rt);
        return Tree[rt].sum;
    }
    push_down(rt);
    ull mid = (l + r)/2;
    ull res = 0;
    if (st <= mid) res += query_Tree(lson, st, ed);
    if (ed >  mid) res += query_Tree(rson, st, ed);
    push_up(rt);
    return res;
}

void calc(int x, int y, ull c, int id) {
    while (top[x] != top[y]) {
        if (dep[top[x]] < dep[top[y]]) swap(x,y);
        update_Tree(1, pos[top[x]], pos[x], c, id);
        x = fa[top[x]];
    }
    if(dep[x] > dep[y]) swap(x,y);
    update_Tree(1, pos[x], pos[y], c, id);
}

ull query(int x, int y) {
    ull ans = 0;
    while (top[x] != top[y]) {
        if (dep[top[x]] < dep[top[y]]) swap(x,y);
        ans += query_Tree(1, pos[top[x]], pos[x]);
        x = fa[top[x]];
    }
    if(dep[x] > dep[y]) swap(x,y);
    ans += query_Tree(1, pos[x], pos[y]);
    return ans;
}

int main(){
    Val = -1;
    while (~scanf("%d", &n)) {
        Init();
        for (int i = 2; i <= n; i++) {
            int u;
            scanf("%d", &u);
            add(u, i); add(i, u);
        }
        dfs(1, 1, 1);
        dfs(1, 1);

        build_Tree(1, 1, n);
        scanf("%d", &m);
        while (m--) {
            int opt, u, v;
            ull x;
            scanf("%d", &opt);
            if (opt == 1) {
                scanf("%d", &u);scanf("%d", &v);
                scanf("%llu", &x);
                calc(u, v, x, 0);
            } else if (opt == 2) {
                scanf("%d", &u);scanf("%d", &v);
                scanf("%llu", &x);
                calc(u, v, x, 1);
            } else if (opt == 3) {
                scanf("%d", &u);scanf("%d", &v);
                calc(u, v, -1, 0);
                calc(u, v, Val, 1);
            } else {
                scanf("%d", &u);scanf("%d", &v);
                printf("%llu\n", query(u, v));
            }
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值