2018ICPC网络赛(焦作站)E题题解

一、题目链接

二、题意

给定一棵树,有四种操作:

$1\ u\ v\ x$:把节点$u$到$v$路径上的所有点的权值乘以$x$;

$2\ u\ v\ x$:把节点$u$到$v$路径上的所有点的权值加上$x$;

$3\ u\ v$:把节点$u$到$v$路径上的所有点的权值取反(~操作);

$4\ u\ v$:查询节点$u$到$v$路径上的所有点的权值和;

所有操作都需要$mod\ 2^{64}$。

三、思路

操作1、2和4是很裸的树链剖分。关键是操作3。这里有个小技巧:对任意一个数字$x$取反,都等于$-(x+1)$。对于此题的$unsigned\ long\ long$类型,同样适用($-1$就是$1111\cdots1111_{(2)}=2^{64}-1$)。明白这个以后,取反操作可以变成加法和乘法。

然后要注意的是,线段树的区间加和区间乘的顺序问题,这里也有个技巧:如果区间$[l,r]$之前的和是$sum$,这个区间先加了一个$x$,再乘了一个$y$,那么可以变成$sum*y+x*y$。即让这个区间先乘一个$y$,再加上$x*y$。这样就可以保证以先乘后加的顺序不出问题了。

然后这题就愉快地AC了。

结论:对任意一个数$x$取反,无论这个数的类型是什么($int,unsigned\ int,long\ long,unsigned\ long\ long$都可以),都等于$-(x+1)$。

四、代码

 

#include<bits/stdc++.h>
using namespace std;
#define MAXN 100010
typedef unsigned long long ULL;
struct edge {
    int to, next;
    edge(int to = 0, int next = 0): to(to), next(next) {}
} es[MAXN * 2];
int head[MAXN], ecnt;
int f[MAXN], deep[MAXN], siz[MAXN], zson[MAXN], top[MAXN], o2n[MAXN], dfscnt;
int n, q;
ULL a[MAXN];

namespace st {
    struct node {
        ULL sum, slazy, plazy;
        node(ULL a1 = 0, ULL a2 = 0, ULL a3 = 0): sum(a1), slazy(a2), plazy(a3) {}
    } ns[MAXN * 4];
    void init() {
        for(int i = 0, t = 4 * n + 5; i < t; ++i)ns[i] = node(0, 0, 1);
    }
    void pushdown(int rt, int l, int r) {
        int lch = rt << 1, rch = rt << 1 | 1, mid = (l + r) >> 1;
        if(ns[rt].plazy != 1 || ns[rt].plazy != 0) {
            ns[lch].sum = ns[lch].sum * ns[rt].plazy + (mid - l + 1) * ns[rt].slazy;
            ns[rch].sum = ns[rch].sum * ns[rt].plazy + (r - mid) * ns[rt].slazy;
            ns[lch].plazy *= ns[rt].plazy;
            ns[rch].plazy *= ns[rt].plazy;
            ns[lch].slazy = ns[lch].slazy * ns[rt].plazy + ns[rt].slazy;
            ns[rch].slazy = ns[rch].slazy * ns[rt].plazy + ns[rt].slazy;
            ns[rt].plazy = 1, ns[rt].slazy = 0;
        }
    }
    void update(int ul, int ur, ULL x, int type, int rt = 1, int l = 1, int r = n) {
        if(l > ur || r < ul)return;
        if(l >= ul && r <= ur) {
            if(type == 2)ns[rt].sum += x * ULL(r - l + 1), ns[rt].slazy += x;
            else ns[rt].sum *= x, ns[rt].plazy *= x, ns[rt].slazy *= x;
            return;
        }
        pushdown(rt, l, r);
        int mid = (l + r) >> 1;
        if(ul <= mid)update(ul, ur, x, type, rt << 1, l, mid);
        if(ur > mid)update(ul, ur, x, type, rt << 1 | 1, mid + 1, r);
        ns[rt].sum = ns[rt << 1].sum + ns[rt << 1 | 1].sum;
    }
    ULL query(int ql, int qr, int rt = 1, int l = 1, int r = n) {
        if(l > qr || r < ql)return 0LL;
        if(l >= ql && r <= qr)return ns[rt].sum;
        pushdown(rt, l, r);
        int mid = (l + r) >> 1;
        ULL res = 0;
        if(ql <= mid)res += query(ql, qr, rt << 1, l, mid);
        if(qr > mid)res += query(ql, qr, rt << 1 | 1, mid + 1, r);
        return res;
    }
};
void add(int from, int to) {
    es[++ecnt] = edge(to, head[from]), head[from] = ecnt;
}
void init() {
    memset(head, 0, sizeof(head[0]) * (n + 3));
    ecnt = 0;
    memset(zson, 0, sizeof(zson[0]) * (n + 3));
    dfscnt = 0;
    st::init();
}
void dfs1(int root, int par) {
    deep[root] = deep[par] + 1, f[root] = par, siz[root] = 1;
    int ms = 0;
    for(int i = head[root]; i; i = es[i].next) {
        int to = es[i].to;
        if(to != par) {
            dfs1(to, root);
            siz[root] += siz[to];
            if(ms < siz[to]) {
                ms = siz[to], zson[root] = to;
            }
        }
    }
}
void dfs2(int root, int par, int tp) {
    top[root] = tp, o2n[root] = ++dfscnt;
    st::update(dfscnt, dfscnt, a[root], 2);
    if(!zson[root])return;
    dfs2(zson[root], root, tp);
    for(int i = head[root]; i; i = es[i].next) {
        int to = es[i].to;
        if(to != par && to != zson[root]) {
            dfs2(to, root, to);
        }
    }
}
void update(int u, int v, ULL x, int type) {
    int tu = top[u], tv = top[v];
    while(tu != tv) {
        if(deep[tu] >= deep[tv]) {
            st::update(o2n[tu], o2n[u], x, type);
            u = f[tu], tu = top[u];
        }
        else {
            st::update(o2n[tv], o2n[v], x, type);
            v = f[tv], tv = top[v];
        }
    }
    if(o2n[u] <= o2n[v])st::update(o2n[u], o2n[v], x, type);
    else st::update(o2n[v], o2n[u], x, type);
}
ULL query(int u, int v) {
    int tu = top[u], tv = top[v];
    ULL res = 0;
    while(tu != tv) {
        if(deep[tu] >= deep[tv]) {
            res += st::query(o2n[tu], o2n[u]);
            u = f[tu], tu = top[u];
        }
        else {
            res += st::query(o2n[tv], o2n[v]);
            v = f[tv], tv = top[v];
        }
    }
    if(o2n[u] <= o2n[v])res += st::query(o2n[u], o2n[v]);
    else res += st::query(o2n[v], o2n[u]);
    return res;
}
int main() {
//    freopen("e.in","r",stdin);
    int fa, op, u, v;
    ULL x;
    while(~scanf("%d", &n)) {
        init();
        for(int i = 2; i <= n; ++i) {
            scanf("%d", &fa);
            add(fa, i), add(i, fa);
        }
        dfs1(1, 0);
        dfs2(1, 0, 1);
        scanf("%d", &q);
        while(q--) {
            scanf("%d", &op);
            if(op == 1) {
                scanf("%d%d%llu", &u, &v, &x);
                update(u, v, x, 1);
            }
            else if(op == 2) {
                scanf("%d%d%llu", &u, &v, &x);
                update(u, v, x, 2);
            }
            else if(op == 3) {
                scanf("%d%d", &u, &v);
                update(u, v, 1, 2);
                update(u, v, -1, 1);
            }
            else {
                scanf("%d%d", &u, &v);
                ULL res = query(u, v);
                printf("%llu\n", res);
            }
        }
    }
    return 0;
}

 

转载于:https://www.cnblogs.com/fuzhihong0917/p/9661200.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值