ACM-ICPC 2018 焦作赛区网络预赛 E. Jiu Yuan Wants to Eat 树链剖分 线段树

题目链接:Jiu Yuan Wants to Eat

题目大意

一颗树,n各节点( n105 n ≤ 10 5 )每个节点上有一个值 ai a i ( ai264) a i ≤ 2 64 )
有四种操作
1. 将u到v路径上所有节点值乘以x( x264) x ≤ 2 64 )
2. 将u到v路径上所有节点值加上x( x264) x ≤ 2 64 )
3. 将u到v路径上所有节点的值按位取反
4. 输出u到v路径所有节点值的和
所有操作都是在MOD 264 2 64

思路

很明显是树链剖分加线段树, 124操作都很简单,3有点麻烦,但我们可以注意到操作是在MOD 264 2 64 下的
c++中采用补码表示整数,对一个数取相反数(负数)等于对一个数按位取反加一,所以对一个数按位取反也就相当于对其取相反数(乘以-1)再减一(加上-1),转换成了前两种操作
这样我们只需要树链剖分加线段树就能解决,MOD直接利用ull的自然溢出就好了
线段树更新函数void update(int L, int R, ull mm, ull a, int l, int r, int rt)
:将区间[L, R]乘以mm再加上a
这样操作1:update(L, R, x, 0, 1, n, 1)
操作2:update(L, R, 1, x, 1, n, 1)
操作3:update(L, R, -1, -1, 1, n, 1)
设两个lazy标记mul和inc表示乘法和加法操作

代码

正确通过 2018-09-15 17:49 1900ms 22000kB c++

#include <bits/stdc++.h>
using namespace std;

typedef unsigned long long ull;
const int maxn = 1e5 + 100;

int n, m;

struct edge
{
    int to, nxt;
    edge(int t = 0, int n = 0): to(t), nxt(n) {}
};
edge es[maxn * 2];
int head[maxn];
int siz[maxn], son[maxn], dep[maxn], faz[maxn];
int top[maxn], id[maxn], rid[maxn], dfs_clocks;
inline void addedge(int i, int u, int v)
{
    es[i * 2] = edge(v, head[u]);
    head[u] = i * 2;
    es[i * 2 + 1] = edge(u, head[v]);
    head[v] = i * 2 + 1;
}

void dfs1(int u, int fa, int depth)
{
    dep[u] = depth;
    faz[u] = fa;
    siz[u] = 1;

    for (int i = head[u]; i; i = es[i].nxt)
    {
        int v = es[i].to;
        if (v == fa) continue;
        dfs1(v, u, depth + 1);
        siz[u] += siz[v];
        if (son[u] == 0 || siz[v] > siz[son[u]]) son[u] = v;
    }
}
void dfs2(int u, int tp)
{
    top[u] = tp;
    id[u] = dfs_clocks;
    rid[dfs_clocks++] = u;

    if (son[u]) dfs2(son[u], tp);

    for (int i = head[u]; i; i = es[i].nxt)
    {
        int v = es[i].to;
        if (v != son[u] && v != faz[u]) dfs2(v, v);
    }
}

#define ls l, m, rt<<1
#define rs m+1, r, rt<<1|1
ull sum[maxn << 2], mul[maxn << 2], inc[maxn << 2];

void pushUp(int rt)
{
    sum[rt] = sum[rt << 1] + sum[rt << 1 | 1];
}
void pushDown(int rt, int m)
{
    if (mul[rt] != 1)
    {
        mul[rt << 1] *= mul[rt];
        inc[rt << 1] *= mul[rt];
        mul[rt << 1 | 1] *= mul[rt];
        inc[rt << 1 | 1] *= mul[rt];

        sum[rt << 1] *= mul[rt];
        sum[rt << 1 | 1] *= mul[rt];

        mul[rt] = 1;
    }
    if (inc[rt])
    {
        inc[rt << 1] += inc[rt];
        inc[rt << 1 | 1] += inc[rt];

        sum[rt << 1] += (m - m / 2) * inc[rt];
        sum[rt << 1 | 1] += (m / 2) * inc[rt];

        inc[rt] = 0;
    }
}
void build(int l, int r, int rt)
{
    mul[rt] = 1;
    inc[rt] = 0;
    sum[rt] = 0;
    if (l == r) return ;
    int m = (l + r) / 2;
    build(ls);
    build(rs);
    pushUp(rt);
}

void update(int L, int R, ull mm, ull a, int l, int r, int rt)
{
    if (L <= l && r <= R)
    {
        mul[rt] *= mm;
        inc[rt] *= mm;
        inc[rt] += a;

        sum[rt] *= mm;
        // sum[rt] += a;
        sum[rt] += a*(r-l+1);
        return ;
    }
    pushDown(rt, r - l + 1);
    int m = (l + r) / 2;
    if (L <= m) update(L, R, mm, a, ls);
    if (R > m) update(L, R, mm, a, rs);
    pushUp(rt);
}

ull query(int L, int R, int l, int r, int rt)
{
    if (L <= l && r <= R)
    {
        return sum[rt];
    }
    pushDown(rt, r - l + 1);
    int m = (l + r) / 2;
    ull ret = 0;
    if (L <= m) ret += query(L, R, ls);
    if (m < R) ret += query(L, R, rs);
    return ret;
}

void update_path(int x, int y, ull m, ull a)
{
    while (top[x] != top[y])
    {
        if (dep[top[x]] < dep[top[y]]) swap(x, y);
        // update_range(id[top[x]], id[x], z);
        update(id[top[x]], id[x], m, a, 1, n, 1);
        x = faz[top[x]];
    }
    if (dep[x] > dep[y]) swap(x, y);
    update(id[x], id[y], m, a, 1, n, 1);
}


ull query_path(int x, int y)
{
    ull ret = 0;
    while (top[x] != top[y])
    {
        if (dep[top[x]] < dep[top[y]]) swap(x, y);
        // ret = max(ret, query(id[top[x]], id[x], 1, n, 1));
        ret += query(id[top[x]], id[x], 1, n, 1);
        x = faz[top[x]];//error: x = faz[x];
    }

    if (dep[x] > dep[y]) swap(x, y);
    ret += query(id[x], id[y], 1, n, 1);
    return ret;
}


int main()
{
    while (scanf("%d", &n) == 1)
    {
        memset(head, 0, sizeof(head));
        memset(son, 0, sizeof(son));
        dfs_clocks = 1;
        int b;
        for (int i = 2; i <= n; ++i)
        {
            scanf("%d", &b);
            addedge(i, b, i);
        }
        dfs1(1, 1, 1);
        dfs2(1, 1);

        build(1, n, 1);

        scanf("%d", &m);
        for (int i = 0; i < m; ++i)
        {
            int op;
            int u, v;
            ull x;
            scanf("%d", &op);
            if (op == 1)
            {
                scanf("%d%d%llu", &u, &v, &x);
                update_path(u, v, x, 0);
            }
            else if (op == 2)
            {
                scanf("%d%d%llu", &u, &v, &x);
                update_path(u, v, 1, x);
            }
            else if (op == 3)
            {
                scanf("%d%d", &u, &v);
                update_path(u, v, -1, -1);
            }
            else
            {
                scanf("%d%d", &u, &v);
                printf("%llu\n", query_path(u, v));
            }
        }
    }

    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值