P3261 [JLOI2015] 城池攻占

题链 P3261 [JLOI2015] 城池攻占

题面太长就不复述了,这是一道左偏树的题目,经过简要分析,我们得知对于一个骑士来说,他所走的路径是唯一且单调的,因此我们可以考虑通过拓扑序来遍历。

对于每个点维护一个左偏树,存储当前节点的骑士的参数,那么一个节点的骑士可以由若干子节点的左偏树合并而来。

由于 \(a\)\(v\) 的存在会使得我们骑士参数改变,但我们不可能遍历每一个骑士修改它的参数,我们此时可以采用懒标记保存信息。

每个城市骑士战死的个数可以通过弹出堆顶来得出,由于骑士在树上始终向根节点攻城,所以处理出每个骑士的初始点和终止点求深度差即可。

利用左偏树,可以做到时间复杂度 \(O(n \log n)\)

参考代码

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 3e5 + 10;
int n, m, f[N], c[N], dep[N], death[N];
int root[N];
ll h[N], a[N], v[N];

struct Node {
    ll v, add, mul;
    int l, r, dist, city;
}tr[N];

bool cmp(int x, int y) {
    if (tr[x].v != tr[y].v) return tr[x].v > tr[y].v;
    return x > y;
}

void pushdown(int u) {
    if (tr[u].l) {
        tr[tr[u].l].mul *= tr[u].mul;
        tr[tr[u].l].add *= tr[u].mul;
        tr[tr[u].l].add += tr[u].add;
        tr[tr[u].l].v *= tr[u].mul;
        tr[tr[u].l].v += tr[u].add;
    }
    if (tr[u].r) {
        tr[tr[u].r].mul *= tr[u].mul;
        tr[tr[u].r].add *= tr[u].mul;
        tr[tr[u].r].add += tr[u].add;
        tr[tr[u].r].v *= tr[u].mul;
        tr[tr[u].r].v += tr[u].add;
    }
    tr[u].mul = 1;
    tr[u].add = 0;
}

int merge(int x, int y) {
    if (!x || !y) return x | y;
    if (cmp(x, y)) swap(x, y);
    pushdown(x), pushdown(y);
    tr[x].r = merge(tr[x].r, y);
    if (tr[tr[x].l].dist < tr[tr[x].r].dist) swap(tr[x].l, tr[x].r);
    tr[x].dist = tr[tr[x].r].dist + 1;
    return x;
}

void pop(int &x) {
    pushdown(x);
    x = merge(tr[x].l, tr[x].r);
}

void modify(int u, int x) {
    if (!a[x]) {
        tr[u].v += v[x];
        tr[u].add += v[x];
    }
    else {
        tr[u].v *= v[x];
        tr[u].mul *= v[x];
        tr[u].add *= v[x];
    }
}

int main() {
    ios::sync_with_stdio(false);
    cin.tie(0), cout.tie(0);
    cin >> n >> m;
    dep[0] = -1;
    for (int i = 1; i <= n; i ++ ) cin >> h[i];
    for (int i = 2; i <= n; i ++ ) cin >> f[i] >> a[i] >> v[i], dep[i] = dep[f[i]] + 1;
    for (int i = 1; i <= m; i ++ ) {
        cin >> tr[i].v >> c[i], tr[i].mul = 1;
        root[c[i]] = merge(root[c[i]], i);
    }
    for (int i = n; i; i -- ) {
        while (root[i] && h[i] > tr[root[i]].v) {
            death[i] ++ ;
            tr[root[i]].city = i;
            pop(root[i]);
        }
        if (i == 1) break;
        modify(root[i], i);
        root[f[i]] = merge(root[f[i]], root[i]);
    }
    for (int i = 1; i <= n; i ++ ) cout << death[i] << "\n";
    for (int i = 1; i <= m; i ++ ) cout << dep[c[i]] - dep[tr[i].city] << "\n";
    return 0;
}
posted @ 2025-03-03 19:27  YipChip  阅读(36)  评论(0)    收藏  举报