ACM-ICPC 2018 焦作赛区网络预赛 Jiu Yuan Wants to Eat

2 篇文章 0 订阅
1 篇文章 0 订阅

原题传送门

题意:
给定一颗树, 每个树节点的起始点权为64位无符号位的0,然后针对这棵树有以下4种操作

1 u v x : u到v的路径上所有的点权都乘以x
2 u v x : u到v的路径上所有的点权都加上x
3 u v : u到v的路径上所有的点权都进行64位取反
4 u v : u到v的路径上的点权和

答案对2^64取模, x < 2^64

思路:
树的大小1e5, 并且不破坏树的结构而对树的属性(点权)进行修改,因此联想到树链剖分
但是比赛的时候不知道要怎么处理3操作
要是没有3操作一下子就知道这是一个区间更新,区间查询的树链剖分 + 双标记的线段树了
那么关于取反, 有这样一个知识点, 负数的补码转换(反正我是想不到的)

-x = (~x) + 1
~x = -x - 1

就这样把3操作转换成了 乘以 -1, 再加上 -1.

AC代码

#include <bits/stdc++.h>
#define pb push_back
#define lc (i << 1)
#define rc (i << 1 | 1)
#define mid (l + r >> 1)
using namespace std;
typedef unsigned long long ull;

const int N = 1e5 + 10;

vector<int> g[N];
int fa[N], sz[N], deep[N], son[N];
int p[N], top[N], dfsp;
ull sum[N << 2], add[N << 2], mul[N << 2];
int n, m;

void dfs1(int u, int dep)
{
    sz[u] = 1;
    deep[u] = dep;
    son[u] = -1;
    for(auto i : g[u])
    {
        dfs1(i, dep + 1);
        sz[u] += sz[i];
        if(son[u] == -1 || sz[son[u]] < sz[i])
            son[u] = i;
    }
}

void dfs2(int u, int  Top)
{
    top[u] = Top;
    p[u] = ++ dfsp;
    if(son[u] + 1)
        dfs2(son[u], Top);
    for(auto i : g[u])
        if(i != son[u])
            dfs2(i, i);
}

void push_up(int i)
{
    sum[i] = sum[lc] + sum[rc];
}

void push_down(int l,  int r, int i)
{
    if(mul[i] - 1)
    {
        sum[lc] *= mul[i];
        mul[lc] *= mul[i];
        add[lc] *= mul[i];
        sum[rc] *= mul[i];
        mul[rc] *= mul[i];
        add[rc] *= mul[i];
        mul[i] =  1;
    }
    if(add[i])
    {
        sum[lc] += add[i] * (ull)(mid - l + 1);
        add[lc] += add[i];
        sum[rc] += add[i] * (ull)(r - mid);
        add[rc] += add[i];
        add[i] = 0;
    }
}

void build(int l, int r, int i = 1)
{
    add[i] = sum[i] = 0;
    mul[i] = 1;
    if(l == r)  return ;
    build(l, mid, lc);
    build(mid + 1, r, rc);
}

void update(int ll, int rr, int l, int r, bool flag, ull x, int i = 1)
{
    if(ll <= l && r <= rr)
    {
        if(flag)
        {
            add[i] *= x;
            mul[i] *= x;
            sum[i] *= x;
        }
        else
        {
            sum[i] += x * (ull)(r - l + 1);
            add[i] += x;
        }
        return ;
    }
    push_down(l, r, i);
    if(ll <= mid)
        update(ll, rr, l, mid, flag, x, lc);
    if(rr > mid)
        update(ll, rr, mid + 1, r, flag, x, rc);
    push_up(i);
}

ull query(int ll, int rr, int l, int r, int i = 1)
{
    if(ll <= l && r <= rr)
    {
        return sum[i];
    }
    push_down(l, r, i);
    ull ans = 0;
    if(ll <= mid)
        ans += query(ll, rr, l, mid, lc);
    if(rr > mid)
        ans += query(ll, rr, mid + 1, r, rc);
    return ans;
}

void interval(int u, int v, bool flag, ull x)
{
    int a = top[u], b = top[v];
    while(a != b)
    {
        if(deep[a] < deep[b])
        {
            swap(a, b);
            swap(u, v);
        }
        update(p[a], p[u], 1, n, flag, x);
        u = fa[a];
        a = top[u];
    }
    if(deep[u] < deep[v])
        swap(u, v);
    update(p[v], p[u], 1, n, flag, x);
}

ull get_ans(int u, int v)
{
    int a = top[u], b = top[v];
    ull ans = 0;
    while(a != b)
    {
        if(deep[a] < deep[b])
        {
            swap(a, b);
            swap(u, v);
        }
        ans += query(p[a], p[u], 1, n);
        u = fa[a];
        a = top[u];
    }
    if(deep[u] < deep[v])
        swap(u, v);
    ans += query(p[v], p[u], 1, n);
    return ans;
}

int main()
{
//    freopen("1.txt", "r", stdin);
    while(~scanf("%d", &n))
    {
        for(int i = 1; i <= n; i ++)
            g[i].clear();
        for(int i = 2; i <= n; i ++)
        {
            scanf("%d", fa + i);
            g[fa[i]].pb(i);
        }
        dfs1(1, 0);
        dfsp = 0;
        dfs2(1, 1);
        build(1, n);

        scanf("%d", &m);
        while(m --)
        {
            int op, u, v;
            ull x;
            scanf("%d%d%d",&op, &u, &v);

            if(op <= 2)
            {
                scanf("%llu", &x);
                interval(u, v, op & 1, x);
            }
            else if(op == 3)
            {
                interval(u, v, 1, (ull)-1);
                interval(u, v, 0, (ull)-1);
            }
            else
                printf("%llu\n", get_ans(u, v));
        }
    }
    return 0;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值