「JLOI2015」城池攻占(左偏树)

题目描述

小铭铭最近获得了一副新的桌游,游戏中需要用 m 个骑士攻占 n 个城池。

这 n 个城池用 1 到 n 的整数表示。除 1 号城池外,城池 i 会受到另一座城池 fi 的管辖,其中 fi<i。也就是说,所有城池构成了一棵有根树。这 m 个骑士用 1 到 m 的整数表示,其中第 i 个骑士的初始战斗力为 si,第一个攻击的城池为 ci

每个城池有一个防御值 hi,如果一个骑士的战斗力大于等于城池的生命值,那么骑士就可以占领这座城池;否则占领失败,骑士将在这座城池牺牲。占领一个城池以后,骑士的战斗力将发生变化,然后继续攻击管辖这座城池的城池,直到占领 1 号城池,或牺牲为止。

除 1 号城池外,每个城池 i 会给出两个战斗力变化参数 ai, vi。若 ai=0,攻占城池 i 以后骑士战斗力会增加 vi;若 ai=1,攻占城池 i 以后,战斗力会乘以 vi。注意每个骑士是单独计算的。也就是说一个骑士攻击一座城池,不管结果如何,均不会影响其他骑士攻击这座城池的结果。

现在的问题是,对于每个城池,输出有多少个骑士在这里牺牲;对于每个骑士,输出他攻占的城池数量。

Solution

看到大于等于某个数很容易想到堆,看到是树上问题很容易想到可并堆。

每个点维护一棵左偏树,存储在这个点的骑士。然后从下往上更新。

然后把所有小于城池生命值的骑士pop掉,同时记录有多少骑士死在这个城池了(ans1),哪些死了的骑士占领了几个城池(ans2)。

我们发现每座城池被占领后还会改变骑士的攻击力。其实这只要打一个懒标记在堆顶即可。

每次pop,和merge之前都要把懒标记下放进去。具体规则同线段树。

#include <iostream>
#include <cstdio>
#define int long long
using namespace std;
const int N = 300010;
const int M = 300010;
struct Leftist_Tree{
    int val, dist, lson, rson, add, mul;
}tr[N];
struct node{
    int pre, to;
}edge[N];
int head[N], tot;
int n, m;
int dep[N];
int h[N];
int fa[N], a[N], V[N];
int s[M], c[M];
int rt[N];
int ans1[N], ans2[M];
void add(int x, int v) {
    tr[x].val += v;
    tr[x].add += v;
}
void mul(int x, int v) {
    tr[x].val *= v;
    tr[x].add *= v;
    tr[x].mul *= v;
}
void push_down(int u) {
    if (tr[u].mul > 1) {
        mul(tr[u].lson, tr[u].mul);
        mul(tr[u].rson, tr[u].mul);
        tr[u].mul = 1;
    }
    if (tr[u].add) {
        add(tr[u].lson, tr[u].add);
        add(tr[u].rson, tr[u].add);
        tr[u].add = 0;
    }
}
int merge(int u, int v) {
    if (!u || !v) return u | v;
    if (tr[u].val > tr[v].val) swap(u, v);
    push_down(u);
    push_down(v);
    tr[u].rson = merge(tr[u].rson, v);
    if (tr[tr[u].lson].dist < tr[tr[u].rson].dist) swap(tr[u].lson, tr[u].rson);
    tr[u].dist = tr[tr[u].rson].dist + 1;
    return u;
}
int pop(int u) {
    push_down(u);
    return merge(tr[u].lson, tr[u].rson);
}
void dfs(int x) {
    for (int i = head[x]; i; i = edge[i].pre) {
        int y = edge[i].to;
        dep[y] = dep[x] + 1;
        dfs(y);
        rt[x] = merge(rt[y], rt[x]);
    }
    while (rt[x] && tr[rt[x]].val < h[x]) {
        ans1[x]++;
        ans2[rt[x]] = dep[c[rt[x]]] - dep[x];
        rt[x] = pop(rt[x]);
    }
    if (a[x]) {
        mul(rt[x], V[x]);
    } else {
        add(rt[x], V[x]);
    }
    if (x == 1) {
        while (rt[x]) {
            ans2[rt[x]] = dep[c[rt[x]]] + 1;
            rt[x] = pop(rt[x]);
        }
    }
}
void Add(int u, int v) {
    edge[++tot] = node{head[u], v};
    head[u] = tot;
}
int read() {
    int ret = 0, f = 1;
    char ch = getchar();
    while (!isdigit(ch)) {
        if (ch == '-') f = -1;
        ch = getchar();
    }
    while (isdigit(ch)) {
        ret = (ret << 1) + (ret << 3) + ch - '0';
        ch = getchar();
    }
    return ret * f;
}
void write(int x) {
    if (x > 9) write(x / 10);
    putchar(x % 10 + '0');
}
void print(int x) {
    if (x < 0) {
        x = -x;
        putchar('-');
    }
    write(x);
    putchar('
');
}
#undef int
int main() {
    #define int long long 
    n = read();
    m = read();
    for (int i = 1; i <= n; i++) h[i] = read();
    for (int i = 2; i <= n; i++) {
        fa[i] = read();
        a[i] = read();
        V[i] = read();
        Add(fa[i], i);
    }
    for (int i = 1; i <= m; i++) {
        s[i] = read();
        c[i] = read();
        tr[i].add = tr[i].dist = tr[i].lson = tr[i].rson = 0;
        tr[i].mul = 1;
        tr[i].val = s[i];
        rt[c[i]] = merge(i, rt[c[i]]);
    }
    dfs(1);
    for (int i = 1; i <= n; i++) print(ans1[i]);
    for (int i = 1; i <= m; i++) print(ans2[i]);
    return 0;
}
原文地址:https://www.cnblogs.com/zcr-blog/p/12864648.html