HUT-XXXX Money out of Thin Air 线段树

该题题义是给定一个公司的结构,然后针对每个员工的一系列操作。由于问题牵涉到一个员工以及一个员工所领导的部门,因此用普通线性结构显然效率太低。这里用到了线段树。

step1:首先我们要确定题目是要对给定的点或者是区间进行操作,那么我们自然而然会想到线段树,但是这题还不是直接的线段树,需要对题中给定的关系进行离散话,也就是按
    照先序或者是后序遍历进行排序,这样某一个员工所领导的部门下的成员就在物理上连续了,并且我们用两个数组分别记录某个成员所领导的部门的左边界和右边界。

step2:有了上一步的操作后,我们就可以进行构树了,按照普通的线段树进行构建,线段树仅仅只是我们处理点和区间的一种数据结构,其本身没有什么实际意义,记得用一个映
    射将线段树的点和实际员工的编号联系起来,这样方便了初始化,也便于最后的输出。

step3:此时我们就可以来处理数据中所给定的操作了,点更新和区间更新,当然这里可以用到lazy标记。

PS:由于整个数很可能退化成链表,所以最好使用非递归来实现先序或者是后序遍历。

代码如下:

#include <cstring>
#include <cstdio>
#include <cstdlib>
#include <algorithm>
#include <queue>
#define MAXN 50005
using namespace std;

int N, Q, s[MAXN], stk[MAXN], visit[MAXN], top;
int seq[MAXN], idx, L[MAXN], R[MAXN];
typedef long long int Int64;

queue<int>q[50005];

struct Node
{
    int l, r;
    Int64 sum, lazy;
}e[200005];

void getseq(int boss) {  
    int pos;
    idx = -1;
    top = 1;
    stk[top] = boss;
    memset(visit, 0, sizeof (visit));
    while (top) {
        pos = stk[top];
        if (!visit[pos]) {
            visit[pos] = 1;
            seq[++idx] = pos; 
            L[pos] = idx; 
        }
        if (!q[pos].empty()) { 
            stk[++top] = q[pos].front();
            q[pos].pop();
        }
        else {
            R[pos] = idx; 
            --top;
        }
    }
}

void build(int p, int l, int r)
{ 
    e[p].l = l, e[p].r = r;
    e[p].lazy = 0;
    if (l!= r) {
        int mid = (l + r) >> 1;
        build(p<<1, l, mid);
        build(p<<1|1, mid+1, r);
        e[p].sum = e[p<<1].sum + e[p<<1|1].sum;
    }
    else {  // l == r 
        e[p].sum = s[seq[l]]; 
    }
}


inline void update(int p)
{
    e[p].sum = e[p<<1].sum + e[p<<1|1].sum;
}

Int64 sum(int p, int l, int r)
{
    if (e[p].l == l && r == e[p].r) {
        return e[p].sum;
    }
    int mid = (e[p].l + e[p].r) >> 1;
    if (e[p].lazy) {
        e[p<<1].lazy += e[p].lazy;
        e[p<<1|1].lazy += e[p].lazy;
        e[p<<1].sum += (e[p<<1].r-e[p<<1].l+1)*e[p].lazy;
        e[p<<1|1].sum += (e[p<<1|1].r-e[p<<1|1].l+1)*e[p].lazy;
        e[p].lazy = 0;
    }
    if (r <= mid) {
        return sum(p<<1, l, r);
    }
    else if (l > mid) {
        return sum(p<<1|1, l, r);
    }
    else {
        return sum(p<<1, l, mid) + sum(p<<1|1, mid+1, r);
    }
}

void modify(int p, int l, int r, Int64 x)
{
    if (e[p].l == l && r == e[p].r) {
        e[p].lazy += x;
        e[p].sum += (e[p].r-e[p].l+1) * x;
        // 身上有延时标记,但是本身的值一定要是正确的
        return;
    }
    int mid = (e[p].l + e[p].r) >> 1;
    if (e[p].lazy) {
        e[p<<1].lazy += e[p].lazy;
        e[p<<1|1].lazy += e[p].lazy;
        e[p<<1].sum += (e[p<<1].r-e[p<<1].l+1)*e[p].lazy;
        e[p<<1|1].sum += (e[p<<1|1].r-e[p<<1|1].l+1)*e[p].lazy;
        e[p].lazy = 0;
    }
    if (r <= mid) {
        modify(p<<1, l, r, x);
    }
    else if (l > mid) {
        modify(p<<1|1, l, r, x);
    }
    else {
        modify(p<<1, l, mid, x);
        modify(p<<1|1, mid+1, r, x);
    }
    update(p);
}

int main()
{ 
    int p, x, y, z, first = 1;
    Int64 level;
    char op[15];
    while (scanf("%d %d %d", &N, &Q, &s[0]), N|Q|s[0]) {
        if (first) {
            first = 0;
        }
        else {
            puts("");
        }
        for (int i = 1; i < N; ++i) {
            scanf("%d %d", &p, &s[i]);
            q[p].push(i); 
        } 
        getseq(0);
        build(1, 0, N-1);  // 区间划分是 0 - N-1
        while (Q--) {
            scanf("%s %d %d %d", op, &x, &y, &z);
            if (op[0] == 'e') {
                level = sum(1, L[x], L[x]);
                if (level < y) {
                    modify(1, L[x], L[x], z);
                }
            }
            else {
                level = sum(1, L[x], R[x]);
                if (1.*level/((R[x]-L[x]+1)) < y) {
                    modify(1, L[x], R[x], z);
                }
            }
        }
        for (int i = 0; i < N; ++i) {
            printf("%lld\n", sum(1, L[i], L[i]));
        }
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值