【JLOI 2015】城池攻占

Solution

现在感觉自己的眼睛要瞎了。

我们发现这东西好像挺适合左偏树的啊:骑士在儿子节点死了就不会走到父亲节点,可以直接删去。

考虑到这条性质(而且城池本来就是树),我们在每个城池建一颗关于骑士的树,表示这一坨骑士都可以来到这个城池。

我们把最小值作为骑士的根,那么就可以删根然后统计城池的死亡骑士和骑士到哪座城池。

关于战斗力,我们把骑士树进行一个 lazy 标记就行了,因为我们是从根开始删除的。

注意要考虑挺到最后的骑士。

Code

#include <cmath>
#include <cstdio>
#include <iostream>
using namespace std;
typedef long long ll;

const int N = 3e5 + 5;

int n, m, head[N], dot[N << 1], nxt[N << 1], cnt;
ll ans[N], res[N];

void addEdge(const int u, const int v) {
    dot[++ cnt] = v; nxt[cnt] = head[u]; head[u] = cnt;
}

ll read() {
    ll x = 0, f = 1; char s;
    while((s = getchar()) < ‘0‘ || s > ‘9‘) if(s == ‘-‘) f = -1;
    while(s >= ‘0‘ && s <= ‘9‘) {x = (x << 1) + (x << 3) + (s ^ 48); s = getchar();}
    return x * f;
}

struct LT {
    int f[N], son[N][2], dis[N], b[N], dep[N], a[N];
    ll val[N], mul[N], add[N], h[N], Val[N];

    void sign(const int o, const ll Mul, const ll Add) {
        if(! o) return;
        val[o] *= Mul; val[o] += Add;
        mul[o] *= Mul; add[o] *= Mul; add[o] += Add;
    }

    void pushDown(const int o) {
        sign(son[o][0], mul[o], add[o]);
        sign(son[o][1], mul[o], add[o]);
        mul[o] = 1, add[o] = 0;
    }

    int unite(int x, int y) {
        if(! x || ! y) return x | y;
        pushDown(x), pushDown(y);
        if(val[x] > val[y]) swap(x, y);
        son[x][1] = unite(son[x][1], y);
        if(dis[son[x][0]] < dis[son[x][1]]) swap(son[x][0], son[x][1]);
        dis[x] = dis[son[x][1]] + 1;
        return x;
    }

    void init() {
        dep[1] = 1;
        for(int i = 1; i <= n; ++ i) {
            son[i][0] = son[i][1] = dis[i] = 0;
        }
        int x;
        for(int i = 1; i <= n; ++ i) h[i] = read();
        for(int i = 2; i <= n; ++ i) {
            x = read(), a[i] = read(), Val[i] = read();
            addEdge(x, i);
        }
        for(int i = 1; i <= m; ++ i) {
            val[i] = read(), b[i] = read();
            mul[i] = 1;
            f[b[i]] = unite(f[b[i]], i);
        }
    }

    int del(const int x) {
        int l = son[x][0], r = son[x][1];
        return unite(l, r);
    }

    void solve(const int u) {
        for(int i = head[u]; i; i = nxt[i]) {
            int v = dot[i];
            dep[v] = dep[u] + 1; solve(v);
            f[u] = unite(f[u], f[v]);
        }
        while(f[u] && val[f[u]] < h[u]) {
            pushDown(f[u]);
            ++ res[u]; ans[f[u]] = dep[b[f[u]]] - dep[u];
            f[u] = del(f[u]);
        }
        if(a[u]) sign(f[u], Val[u], 0);
        else sign(f[u], 1, Val[u]);
    }
}T;

int main() {
    n = read(), m = read();
    T.init(); T.solve(1);
    while(T.f[1]) {
        T.pushDown(T.f[1]);
        ans[T.f[1]] = T.dep[T.b[T.f[1]]];
        T.f[1] = T.del(T.f[1]);
    }
    for(int i = 1; i <= n; ++ i) printf("%lld\n", res[i]);
    for(int i = 1; i <= m; ++ i) printf("%lld\n", ans[i]);
    return 0;
}

原文地址:https://www.cnblogs.com/AWhiteWall/p/12627103.html

时间: 2024-11-10 13:12:06

【JLOI 2015】城池攻占的相关文章

bzoj4003[JLOI2015]城池攻占

bzoj4003[JLOI2015]城池攻占 题意: 有n个城池组成根节点为1的树,m个人,当一个人的战斗力大于等于攻打城市的防御力,就能攻占这个城市,来到这个城市的父节点,否则该人会牺牲在这个城市.当一个城市被攻占时,会使攻占的人的战斗力加或乘上某个数.现在给出m个人的最开始攻打的城市和初始战斗力,求在每个城市的牺牲人数和每个人一共攻打几个城市.注意这m个人处在不同的时空,即攻击互不影响,且每个人会一直往上攻打除非牺牲或到达根节点. 题解: 由于对一些数乘一个正数或加一个数这些数的相对大小不变

【BZOJ】【4003】【JLOI2015】城池攻占

可并堆 QAQ改了一下午……最终弃疗求助zyf……居然被秒了QAQ真是弱到不行(zyf太神了Orz) 还是先考虑部分分的做法: 1.$n,m\leq 3000$:可以暴力模拟每个骑士的攻打过程,也可以利用拓扑序,将当前城池的后代的攻打情况统计完后,再统计有哪些其实打到了当前城池,over了几个,又有几个继续前进了……时间复杂度应该是O(n*m)的吧. 2.一条链的情况 >_>没想出来 3.所有的骑士武力值都不变的情况:可以用倍增搞出每个骑士如果想打到第$2^k$个祖先处最小需要多大的武力值(其

BZOJ 4003([JLOI2015]城池攻占-带标记可合并堆)[Template:带标记可合并堆]

4003: [JLOI2015]城池攻占 Time Limit: 10 Sec  Memory Limit: 128 MB Submit: 490  Solved: 181 [Submit][Status][Discuss] Description 小铭铭最近获得了一副新的桌游,游戏中需要用 m 个骑士攻占 n 个城池. 这 n 个城池用 1 到 n 的整数表示.除 1 号城池外,城池 i 会受到另一座城池 fi 的管辖, 其中 fi <i.也就是说,所有城池构成了一棵有根树.这 m 个骑士用

【BZOJ 4003】 [JLOI2015]城池攻占

4003: [JLOI2015]城池攻占 Time Limit: 10 Sec Memory Limit: 128 MB Submit: 206 Solved: 89 [Submit][Status][Discuss] Description 小铭铭最近获得了一副新的桌游,游戏中需要用 m 个骑士攻占 n 个城池. 这 n 个城池用 1 到 n 的整数表示.除 1 号城池外,城池 i 会受到另一座城池 fi 的管辖, 其中 fi 小于i.也就是说,所有城池构成了一棵有根树.这 m 个骑士用 1

[洛谷P3261] [JLOI2015]城池攻占(左偏树)

不得不说,这道题目是真的难,真不愧它的"省选/NOI-"的紫色大火题!!! 花了我晚自习前半节课看题解,写代码,又花了我半节晚自习调代码,真的心态爆炸.基本上改得和题解完全一样了我才过了这道题!真的烦.没事,那接下来我来完全把这道题搞透. Part 1 理解题目 至少我一开始不知道为什么要用左偏树,甚至我看题解一开始也都没弄懂,所以先把题目弄清楚. 首先我们由题可以知道,这要求我们从建好的树的叶子节点开始往上推,有些骑士到特定的点才会出现,check一下骑士能否攻占城池,再记录进答案,

【bzoj4003】[JLOI2015]城池攻占 可并堆

题目描述 小铭铭最近获得了一副新的桌游,游戏中需要用 m 个骑士攻占 n 个城池. 这 n 个城池用 1 到 n 的整数表示.除 1 号城池外,城池 i 会受到另一座城池 fi 的管辖,其中 fi <i.也就是说,所有城池构成了一棵有根树.这 m 个骑士用 1 到 m 的整数表示,其中第 i 个骑士的初始战斗力为 si,第一个攻击的城池为 ci.每个城池有一个防御值 hi,如果一个骑士的战斗力大于等于城池的生命值,那么骑士就可以占领这座城池:否则占领失败,骑士将在这座城池牺牲.占领一个城池以后,

BZOJ 4003 【JLOI2015】城池攻占

Description 小铭铭最近获得了一副新的桌游,游戏中需要用 m 个骑士攻占 n 个城池. 这 n 个城池用 1 到 n 的整数表示.除 1 号城池外,城池 i 会受到另一座城池 fi 的管辖, 其中 fi <i.也就是说,所有城池构成了一棵有根树.这 m 个骑士用 1 到 m 的整数表示,其 中第 i 个骑士的初始战斗力为 si,第一个攻击的城池为 ci. 每个城池有一个防御值 hi,如果一个骑士的战斗力大于等于城池的生命值,那么骑士就可 以占领这座城池:否则占领失败,骑士将在这座城池牺

【BZOJ4003】【JLOI2015】城池攻占

Description 小铭铭最近获得了一副新的桌游,游戏中需要用 m 个骑士攻占 n 个城池.这 n 个城池用 1 到 n 的整数表示.除 1 号城池外,城池 i 会受到另一座城池 fi 的管辖,其中 fi <i.也就是说,所有城池构成了一棵有根树.这 m 个骑士用 1 到 m 的整数表示,其中第 i 个骑士的初始战斗力为 si,第一个攻击的城池为 ci. 每个城池有一个防御值 hi,如果一个骑士的战斗力大于等于城池的生命值,那么骑士就可以占领这座城池:否则占领失败,骑士将在这座城池牺牲.占领

bzoj-4003 城池攻占

题意: 给出一个n个结点的有根树,和m个骑士: 树上的结点--城池有一个防御值,骑士有一个战斗力: 当骑士的战斗力大于等于城池时,城池被攻破.骑士的战斗力变化,并向树上的父节点前进: 否则骑士死亡. 求最后每一个城池干掉的人数和每一个人干掉的城数: 骑士之间没有先后关系,就是说事实上每一个骑士是在自己的副本里战斗(笑). n,m<=300000. 题解: 首先依据战斗力变化的规则,从某个结点出发,战斗力弱的能到达哪里,战斗力强的也一定能够. 所以考虑一个结点上的所有骑士.仅仅须要比較防御值和最蒟