【题目大意】

有n个城市构成一棵树,除1号城市外每个城市均有防御值h和战斗变化参量a和v。

现在有m个骑士各自来刷副本,每个其实有一个战斗力s和起始位置c。如果一个骑士的战斗力s大于当前城市的防御值h,则可攻破这个城市,并前往它的管辖地(即树上的父亲),同时,战斗力s发生如下变化:

①如被攻占城市a=0,则s+=v;

②如果a=0,s*=v。

输出:每个骑士能够攻占的城市数量,以及每个城市有多少个骑士牺牲了。

【思路】

昨天写了一遍这道题,当时用的是DFS,今天用拓扑重写一遍。思路如下:

从下往上拓扑排序,左偏树里存放骑士。当前节点上的左偏树相当于可以存活到当前城市的所有骑士的集合,存放在一个小顶堆中。显然,如果较小的骑士能攻破,那么较大的一定能。那么每次,如果堆顶小于当前城市的防御值,则弹出。每个城市攻占结束后,更新一下所有骑士们的战斗力。

左偏树和其它树型结构一样,都可以使用延迟标记。做法和线段树差不多。

延迟标记有三个,lazycnt,lazyadd,lazymul,分别表示攻占城市数的增加和战斗力的增加。更新操作时,将左右孩子的cnt和lazycnt均加上当前的lazycnt。如果当前a=0,则将左右孩子的key和lazyadd加上当前的lazyadd;如果当前a=1,则将左右孩子的key、lazymul和lazyadd均乘以当前的lazymul。

延迟标记在两个地方需要往下推:

①在堆顶元素弹出后。

在merge中将较小根的右子树和较大根的左子树合并的时候。(!!!)

 #include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<vector>
#include<queue>
#define L Ltree[Ltree[x].lson]
#define R Ltree[Ltree[x].rson]
using namespace std;
typedef long long ll;
const int MAXN=+;
struct node
{
ll key;
int dis,pos,cnt;
ll lazyadd,lazymul;int lazycnt;//懒惰标记
int lson,rson;
};
int n,m;
ll h[MAXN],v[MAXN];//防御力,战斗变化量,
int city[MAXN],knight[MAXN],f[MAXN],a[MAXN],out[MAXN],rt[MAXN];
//每个城市牺牲的骑士数,每个骑士攻破的城市数量,管辖地,战斗变化参数,每个节点的出度(拓扑排序使用),到达每个节点位置时的堆顶元素
node Ltree[MAXN];//左偏树
queue<int> que;//树由下至上拓扑排序的队列 void update(int root,int flag,ll delta)
{
Ltree[root].lazycnt++;
Ltree[root].cnt++;
if (flag)
{
Ltree[root].lazyadd*=delta;
Ltree[root].lazymul*=delta;
Ltree[root].key*=delta;
}
else
{
Ltree[root].lazyadd+=delta;
Ltree[root].key+=delta;
}
} void pushdown(int x)
{
if (Ltree[x].lazycnt)
{
L.cnt+=Ltree[x].lazycnt;
R.cnt+=Ltree[x].lazycnt;
L.lazycnt+=Ltree[x].lazycnt;
R.lazycnt+=Ltree[x].lazycnt;
Ltree[x].lazycnt=;
}
if (Ltree[x].lazymul!=)
{
L.key*=Ltree[x].lazymul;
R.key*=Ltree[x].lazymul;
L.lazyadd*=Ltree[x].lazymul;
R.lazyadd*=Ltree[x].lazymul;
L.lazymul*=Ltree[x].lazymul;
R.lazymul*=Ltree[x].lazymul;
Ltree[x].lazymul=;
}
if (Ltree[x].lazyadd)
{
L.key+=Ltree[x].lazyadd;
R.key+=Ltree[x].lazyadd;
L.lazyadd+=Ltree[x].lazyadd;
R.lazyadd+=Ltree[x].lazyadd;
Ltree[x].lazyadd=;
}
} int merge(int x,int y)
{
if (!x||!y)
{
return(x+y);
}
if (Ltree[x].key>Ltree[y].key) swap(x,y);
pushdown(x);
//!!!这里要pushdown!!这里千万不要忘记pushdown!
Ltree[x].rson=merge(Ltree[x].rson,y);
int &l=Ltree[x].lson,&r=Ltree[x].rson;
if (Ltree[l].dis<Ltree[r].dis) swap(l,r);
if (r==) Ltree[x].dis=;
else Ltree[x].dis=Ltree[r].dis+;
return x;
} void init()
{
scanf("%d%d",&n,&m);
memset(rt,,sizeof(rt));
for (int i=;i<=n;i++) scanf("%lld",&h[i]);
for (int i=;i<=n;i++)
{
scanf("%d%d%lld",&f[i],&a[i],&v[i]);
out[f[i]]++;
}
Ltree[].dis=-;
for (int i=;i<=m;i++)
{
ll s;int c;
scanf("%lld%d",&s,&c);
Ltree[i]=(node){s,,i,,,,};
rt[c]=merge(rt[c],i);
}
} void Topology()
{
queue<int> que;
for (int i=;i<=n;i++) if (!out[i]) que.push(i);
while (!que.empty())
{
int u=que.front();que.pop();
int& root=rt[u];
int father=f[u];
while (root && (h[u]>Ltree[root].key))//如果堆顶元素小于城市的防御力,即该骑士会牺牲,则不断弹出
{
knight[Ltree[root].pos]=Ltree[root].cnt;
city[u]++;
pushdown(root);
root=merge(Ltree[root].lson,Ltree[root].rson);
}
update(root,a[u],v[u]);
rt[father]=merge(rt[father],root);
out[father]--;
if (!out[father]) que.push(father);
} while (rt[])//处理所有能够抵达根节点的所有骑士
{
knight[rt[]]=Ltree[rt[]].cnt;
pushdown(rt[]);
rt[]=merge(Ltree[rt[]].lson,Ltree[rt[]].rson);
}
} void printans()
{
for (int i=;i<=n;i++) printf("%d\n",city[i]);
for (int j=;j<=m;j++) printf("%d\n",knight[j]);
} int main()
{
init();
Topology();
printans();
return ;
}

【左偏树+延迟标记+拓扑排序】BZOJ4003-城池攻占的更多相关文章

  1. 【左偏树】【P3261】 [JLOI2015]城池攻占

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

  2. 左偏树初步 bzoj2809 & bzoj4003

    看着百度文库学习了一个. 总的来说,左偏树这个可并堆满足 堆的性质 和 左偏 性质. bzoj2809: [Apio2012]dispatching 把每个忍者先放到节点上,然后从下往上合并,假设到了 ...

  3. 【BZOJ4003】【JLOI2015】城池攻占(左偏树)

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

  4. 左偏树(BZOJ4003)

    左偏树打个标记,没了. #include <cstdio> #include <vector> using namespace std; typedef long long l ...

  5. [luogu3261 JLOI2015] 城池攻占 (左偏树+标记)

    传送门 Description 小铭铭最近获得了一副新的桌游,游戏中需要用 m 个骑士攻占 n 个城池.这 n 个城池用 1 到 n 的整数表示.除 1 号城池外,城池 i 会受到另一座城池 fi 的 ...

  6. P3261 [JLOI2015]城池攻占 (左偏树+标记下传)

    左偏树还是满足堆的性质,节点距离就是离最近的外节点(无左或者右儿子  或者二者都没有)的距离,左偏性质就是一个节点左儿子的距离不小于右儿子,由此得:节点距离等于右儿子的距离+1. 本题就是对于每个节点 ...

  7. 【左偏树+贪心】BZOJ1367-[Baltic2004]sequence

    [题目大意] 给定一个序列t1,t2,...,tn ,求一个递增序列z1<z2<...<zn , 使得R=|t1−z1|+|t2−z2|+...+|tn−zn| 的值最小.本题中,我 ...

  8. 黄源河《左偏树的应用》——数字序列(Baltic 2004)

    这道题哪里都找不到. [问题描述] 给定一个整数序列a1, a2, … , an,求一个不下降序列b1 ≤ b2 ≤ … ≤ bn,使得数列{ai}和{bi}的各项之差的绝对值之和 |a1 - b1| ...

  9. HDU 1512 Monkey King(左偏树+并查集)

    [题目链接] http://acm.hdu.edu.cn/showproblem.php?pid=1512 [题目大意] 现在有 一群互不认识的猴子,每个猴子有一个能力值,每次选择两个猴子,挑出他们所 ...

随机推荐

  1. bufferd对象详解

    使用buffer类处理二进制数据 在客户端javascript脚本代码中,对于二进制数据并没有提供一个很好的支持.然后在nodejs中需要处理像TCP流或文件流时,必须要处理二进制数据.因此在node ...

  2. Spring Boot提供的特性

    一.导览 本文主要按以下模块介绍spring Boot(1.3.6.RELEASE)提供的特性. SpringApplication类 外部化配置 Profiles 日志 开发WEB应用 Securi ...

  3. SQL注入之逗号拦截绕过

    目前所知博主仅知的两个方法 1.通过case when then 2.join [一]case when then mysql,,,,,,, ) ) end; +----+-----------+-- ...

  4. OOM有哪些情况,SOF有哪些情况

    OOM 1.全称为OutOfMemoryError异常,如果虚拟机在扩展栈时无法申请足够的内存空间,抛出它: 2.Java heap异常:java.lang.OutOfMemoryError:Java ...

  5. Vue组件-组件的注册

    注册组件 全局组件 注册组件就是利用Vue.component()方法,先传入一个自定义组件的名字,然后传入这个组件的配置. Vue.component('my-component', { templ ...

  6. python基础===8道基础知识题

    本文转自微信公众号: 2018-03-12 leoxin 菜鸟学Python 原文地址:http://mp.weixin.qq.com/s/JJSDv5YJOZ9e3hn28zWIsQ NO.1 Py ...

  7. H5对安卓WeView开发中的影响

     1.body,或者html 高度为100% 会导致下拉直接触发原生的刷新控件,而不是webView滑动到顶部后刷新,以及不会执行onScrollChanged 方法,并且getScrollY 总是返 ...

  8. 方便大家学习的Node.js教程(一):理解Node.js

    理解Node.js 为了理解Node.js是如何工作的,首先你需要理解一些使得Javascript适用于服务器端开发的关键特性.Javascript是一门简单而又灵活的语言,这种灵活性让它能够经受住时 ...

  9. Nginx-请求处理与响应

    void ngx_http_init_connection(ngx_connection_t *c) { ngx_uint_t i; ngx_event_t *rev; struct sockaddr ...

  10. LightOJ - 1234

    Harmonic Number Time Limit: 3000MS   Memory Limit: 32768KB   64bit IO Format: %lld & %llu Descri ...