【题目大意】

有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. Part2-HttpClient官方教程-Chapter3-HTTP状态管理

    ps:近日忙于课设与一个赛事的准备....时间真紧啊~~ 最初,HTTP被设计为一种无状态的,面向请求/响应的协议,它并没有为跨越多个逻辑相关的请求/响应交换的有状态会话做出特殊规定.随着HTTP协议 ...

  2. Java中基于HotSpot虚拟机的垃圾收集器

    名称 过程 优缺点 Serial 进行垃圾收集时,必须暂停其他所有的工作进程,直到它收集结束.是一个单线程收集器. Stop the world. 新生代收集器. 手工设置新生代的大小:-Xmn Ed ...

  3. Linux System.map文件【转】

    转自:http://blog.csdn.net/ysbj123/article/details/51233618 当运行GNU链接器gld(ld)时若使用了"-M"选项,或者使用n ...

  4. Python基础=== Tkinter Grid布局管理器详解

    本文转自:https://www.cnblogs.com/ruo-li-suo-yi/p/7425307.html          @ 箬笠蓑衣 Grid(网格)布局管理器会将控件放置到一个二维的表 ...

  5. selenium===requestium模块介绍

    有时,你可能会在网上实现一些自动化操作.比如抓取网站,进行应用测试,或在网上填表,但又不想使用API,这时自动化就变得很必要.Python提供了非常优秀的Requests库可以辅助进行这些操作.可惜, ...

  6. Django 国内最全教程

    https://code.ziqiangxuetang.com/django/django-tutorial.html

  7. mysql连接池优化笔记

    中间件mycat是一个高性能的分表分库读写分离的中间件,但配置不好的情况会出现很多性能问题. 1.mycat-web的监控的准确性有问题,1.6-RELEASE  ,1.0-SNAPSHOT (web ...

  8. Centos7 配置网络

    /* Centos7 的网络 不可以用ifconfig获取,需要安装包 所以 .*/ //查看ip [root@master ~]# ip a /* Centos7 的网卡名字与 Centos6有区别 ...

  9. linux 设置开机启动项两种方式

    原文链接:http://blog.csdn.net/karchar/article/details/52489572 有时候我们需要Linux系统在开机的时候自动加载某些脚本或系统服务. 在解问题之前 ...

  10. 小程序 image跟view标签上下会有空隙

    解决方案 就是可以在image那里设置vertical-align:top/bottom/text-top/text-bottom 原因:图片文字等inline元素默许是跟父级元素的baseline对 ...