【题目大意】

有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. margin 居中

    左右auto加个宽度.margin-left: auto; margin-right: auto; width:640px;

  2. docker 环境

    1.docker 升级 步骤: docker -v service docker stop yum -y update docker.io

  3. PKUWC 2019 自闭记

    PKUWC 2019 自闭记 Day -1 考前天天在隔壁的物竞教室划水(雀魂,能和吉老师一起玩的游戏都是好游戏),没有做题. Day 0 早上8:16的高铁,到广州南居然要6个小时...不知道福州和 ...

  4. linux dpm机制分析(下)【转】

    转自:http://blog.csdn.net/lixiaojie1012/article/details/23707901 1      设备注册到dpm_list路径 (Platform_devi ...

  5. gcc -rpath 指定动态库路径

    gcc -rpath 指定动态库路径 http://blog.csdn.net/v6543210/article/details/44809405

  6. 判断cookie创建的时间是否已经24小时

    def read_cookie(self): cookiesfilepath="cookies%s" % self.uid if os.path.exists(cookiesfil ...

  7. MSCL超级工具类(C#),开发人员必备,开发利器

    MSCL超强工具类库 是基于C#开发的超强工具类集合,涵盖了日常B/S或C/S开发的诸多方面,包含上百个常用封装类(数据库操作类全面支持Mysql.Access.Oracle.Sqlserver.Sq ...

  8. swift 动态获取类, 获取命名空间

    在做swift开发中很多时候会动态加载控制器的类, 可以让app更加灵活显示界面信息 一般情况下都是服务器返回显示的控制器类name然后动态显示, 但是服务器返回的类name是string, 怎么转换 ...

  9. python中的map、reduce、filter、sorted函数

    map.reduce.filter.sorted函数,这些函数都支持函数作为参数. map函数 map() 函数语法:map(function, iterable, ...) function -- ...

  10. mac pro 安装mysql并且配置my.cnf(添加默认字符集utf8,数据存放路径,修改已经建好的表的默认字符集等)、mac mysql my.cnf路径

    如果你是还没有下载安装文件,请到官网下载http://dev.mysql.com/downloads/mysql/ 下载好mysql的mac版本的安装文件后解压后将文件放到目录 /usr/local/ ...