题目描述

小铭铭最近获得了一副新的桌游,游戏中需要用 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。注意每个骑士是单独计算的。也就是说一个骑士攻击一座城池,不管结果如何,均不会影响其他骑士攻击这座城池的结果。
现在的问题是,对于每个城池,输出有多少个骑士在这里牺牲;对于每个骑士,输出他攻占的城池数量。

输入

第 1 行包含两个正整数 n;m,表示城池的数量和骑士的数量。

第 2 行包含 n 个整数,其中第 i 个数为 hi,表示城池 i 的防御值。
第 3 到 n +1 行,每行包含三个整数。其中第 i +1 行的三个数为 fi;ai;vi,分别表示管辖
这座城池的城池编号和两个战斗力变化参数。
第 n +2 到 n + m +1 行,每行包含两个整数。其中第 n + i 行的两个数为 si;ci,分别表
示初始战斗力和第一个攻击的城池。

输出

输出 n + m 行,每行包含一个非负整数。其中前 n 行分别表示在城池 1 到 n 牺牲的骑士

数量,后 m 行分别表示骑士 1 到 m 攻占的城池数量。

样例输入

5 5
50 20 10 10 30
1 1 2
2 0 5
2 0 -10
1 0 10
20 2
10 3
40 4
20 4
35 5

样例输出

2
2
0
0
0
1
1
3
1
1

提示

对于 100% 的数据,1 <= n;m <= 300000; 1 <= fi<i; 1 <= ci <= n; -10^18 <= hi,vi,si <= 10^18;ai等于1或者2;当 ai =1 时,vi > 0;保证任何时候骑士战斗力值的绝对值不超过 10^18。

可以发现修改操作只有加一个数和乘一个正数,也就是说两个数的大小关系不会在同时修改后改变。那么我们可以对树上的每个节点维护一个可并堆(小根堆)然后自下而上合并上去。对于树上的一个节点,先将这个点的可并堆与子树的可并堆依次合并,然后判断堆顶是否大于该点的占领权值,如果小于就删除堆顶直到堆顶大于等于该点权值。之后将这个点的堆打上类似线段树的懒惰标记即可。注意乘法标记和加法标记的顺序问题。

#include<set>
#include<map>
#include<queue>
#include<stack>
#include<cmath>
#include<cstdio>
#include<bitset>
#include<vector>
#include<cstring>
#include<iostream>
#include<algorithm>
#define ll long long
using namespace std;
ll h[300010];
int f[300010];
int a[300010];
ll v[300010];
ll s[300010];
int c[300010];
int dis[300010];
int ls[300010];
int rs[300010];
int head[300010];
int to[300010];
int nex[300010];
ll num[300010];
ll sum[300010];
int root[300010];
int res[300010];
int dep[300010];
int ans[300010];
int tot;
int n,m;
void add(int x,int y)
{
nex[++tot]=head[x];
head[x]=tot;
to[tot]=y;
}
void add(int rt,ll k,ll b)
{
if(!rt)
{
return ;
}
s[rt]*=k,s[rt]+=b;
sum[rt]*=k,num[rt]*=k,num[rt]+=b;
}
void pushdown(int rt)
{
add(ls[rt],sum[rt],num[rt]);
add(rs[rt],sum[rt],num[rt]);
sum[rt]=1,num[rt]=0;
}
int merge(int x,int y)
{
if(!x||!y)
{
return x+y;
}
pushdown(x);
pushdown(y);
if(s[x]>s[y])
{
swap(x,y);
}
rs[x]=merge(rs[x],y);
if(dis[ls[x]]<dis[rs[x]])
{
swap(ls[x],rs[x]);
}
dis[x]=dis[rs[x]]+1;
return x;
}
void dfs(int x)
{
for(int i=head[x];i;i=nex[i])
{
dep[to[i]]=dep[x]+1;
dfs(to[i]);
root[x]=merge(root[x],root[to[i]]);
}
if(!root[x])
{
return ;
}
while(s[root[x]]<h[x]&&root[x])
{
pushdown(root[x]);
ans[root[x]]=dep[c[root[x]]]-dep[x];
res[x]++;
root[x]=merge(ls[root[x]],rs[root[x]]);
}
if(a[x])
{
add(root[x],v[x],0);
}
else
{
add(root[x],1,v[x]);
}
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)
{
scanf("%lld",&h[i]);
}
for(int i=2;i<=n;i++)
{
scanf("%d%d%lld",&f[i],&a[i],&v[i]);
add(f[i],i);
}
for(int i=1;i<=m;i++)
{
scanf("%lld%d",&s[i],&c[i]);
sum[i]=1ll;
root[c[i]]=merge(root[c[i]],i);
}
dep[1]=1;
dfs(1);
while(root[1])
{
ans[root[1]]=dep[c[root[1]]];
root[1]=merge(ls[root[1]],rs[root[1]]);
}
for(int i=1;i<=n;i++)
{
printf("%d\n",res[i]);
}
for(int i=1;i<=m;i++)
{
printf("%d\n",ans[i]);
}
}

BZOJ4003[JLOI2015]城池攻占——可并堆的更多相关文章

  1. 【BZOJ4003】[JLOI2015]城池攻占 可并堆

    [BZOJ4003][JLOI2015]城池攻占 Description 小铭铭最近获得了一副新的桌游,游戏中需要用 m 个骑士攻占 n 个城池. 这 n 个城池用 1 到 n 的整数表示.除 1 号 ...

  2. [bzoj4003][JLOI2015]城池攻占_左偏树

    城池攻占 bzoj-4003 JLOI-2015 题目大意:一颗n个节点的有根数,m个有初始战斗力的骑士都站在节点上.每一个节点有一个standard,如果这个骑士的战斗力超过了这个门槛,他就会根据城 ...

  3. BZOJ4003 [JLOI2015]城池攻占 左偏树 可并堆

    欢迎访问~原文出处——博客园-zhouzhendong 去博客园看该题解 题目传送门 - BZOJ4003 题意概括 题意有点复杂,直接放原题了. 小铭铭最近获得了一副新的桌游,游戏中需要用 m 个骑 ...

  4. [BZOJ4003][JLOI2015]城池攻占(左偏树)

    这题有多种做法,一种是倍增预处理出每个点往上走2^i步最少需要的初始战斗力,一种是裸的启发式合并带标记splay. 每个点合并能攻占其儿子的所有骑士,删去所有无法攻占这个城市的骑士并记录答案. 注意到 ...

  5. BZOJ4003 [JLOI2015]城池攻占

    这题有两种做法来着... 第一种就是一开始想到的比较不靠谱,不过貌似可以过掉: 看从$1$号节点开始到$p$号节点最大需要的体力,记录单调上升的体力,询问的时候二分跳着走就可以了 不过精度问题还有可能 ...

  6. BZOJ4003 JLOI2015城池攻占

    用左偏树模拟攻占的过程,维护最小值,最多入和出m次,每次log复杂度. #include<bits/stdc++.h> using namespace std; ; typedef lon ...

  7. BZOJ_4003_[JLOI2015]城池攻占_可并堆

    BZOJ_4003_[JLOI2015]城池攻占_可并堆 Description 小铭铭最近获得了一副新的桌游,游戏中需要用 m 个骑士攻占 n 个城池. 这 n 个城池用 1 到 n 的整数表示.除 ...

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

    不得不说,这道题目是真的难,真不愧它的“省选/NOI-”的紫色大火题!!! 花了我晚自习前半节课看题解,写代码,又花了我半节晚自习调代码,真的心态爆炸.基本上改得和题解完全一样了我才过了这道题!真的烦 ...

  9. BZOJ-4003:城池攻占(可并堆+lazy标记)

    小铭铭最近获得了一副新的桌游,游戏中需要用 m 个骑士攻占 n 个城池. 这 n 个城池用 到 n 的整数表示.除 号城池外,城池 i 会受到另一座城池 fi 的管辖, 其中 fi <i.也就是 ...

随机推荐

  1. .NET Core Community 第三个千星项目诞生:爬虫 DotnetSpider

    本文所有打赏将全数捐赠于 NCC(NCC 的资金目前由 倾竹大人 负责管理),请注明捐赠于 NCC.捐赠情况将由倾竹大人在此处公示. DotnetSpider 至力于打造一个轻量化.高效率.易开发.可 ...

  2. 617. Merge Two Binary Trees(Easy)

    Given two binary trees and imagine that when you put one of them to cover the other, some nodes of t ...

  3. 史上最全 原生javascript的知识总结,适合新手及查资料用!

    适合右键另存为图片保存,再放大看!

  4. django的配置文件字符串是怎么导入的?

    写在开头: 每个APP都会有配置文件,像下代码Django等等这种的settings里面的配置导入都是字符串的,他们是怎么做的呢? MIDDLEWARE = [ 'django.middleware. ...

  5. myecplise ssh项目配置上遇到的问题

    版本:spring3.1+hib4.1+struts2.1 学习项目使用此版本运行时,总是会遇到各样的错误,在这里做一下记录. 问题1:log4j相关 spring的web项目,执行时报错: 信息: ...

  6. <c:forEach varStatus="status">中 varStatus的作用

    varStatus是<c:forEach>jstl循环标签的一个属性,varStatus属性. varStatus=“status”事实上定义了一个status名的对象作为varStatu ...

  7. oracle创建表空间、创建用户、授权角色和导入导出用户数据

    使用数据库管理员身份登录 -- log as sysdba sqlplus / as sysdba; 创建临时表空间 -- create temporary tablespace create tem ...

  8. Postman的Post请求方式的四种类型的数据

    1. form-data 就是http请求中的multipart/form-data,它会将表单的数据处理为一条消息,以标签为单元,用分隔符分开.既可以上传键值对,也可以上传文件.当上传的字段是文件时 ...

  9. [转帖]LCD与LED的区别之背光原理与优缺点对比介绍

    LCD与LED的区别之背光原理与优缺点对比介绍 http://m.elecfans.com/article/620376.html 时下液晶面板与液晶电视技术已经达到炉火纯青的境界,并已经成为大屏幕平 ...

  10. Oracle 备份表数据

    --备份表数据 select * from t_owners; --创建备份表 create table t_owners_copy ( id number, name ), addressid nu ...