【题目大意】

有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. jq_常用方法

    //获取兄弟元素 $('.class').siblings() 当前元素所有的兄弟节点 $('.class').prev() 当前元素前一个兄弟节点 $('.class').prevaAll() 当前 ...

  2. 变量对象vs活动对象

    这是我见过描述的最为详尽的关于变量对象.活动对象以及闭包的解析,来自知乎,感谢答主: 作者:闭家锁链接:https://www.zhihu.com/question/36393048/answer/7 ...

  3. Android应用程序App应用上线流程

    对于很多初级开发者,可能对app应用上线不太了解,本文跟大家介绍一下怎么上线app应用.上线App并不是一件很困难的事情,App的应用功能也不需要很强大,甚至不用联网,只有简单的一两个页面的App应用 ...

  4. 【swupdate文档 一】嵌入式系统的软件管理

    嵌入式系统的软件管理 嵌入式系统变得越来越复杂, 它们的软件也反映了这种复杂性的增加. 为了支持新的特性和修复,很有必要让嵌入式系统上的软件 能够以绝对可靠的方式更新. 在基于linux的系统上,我们 ...

  5. $(document).ready 和 window.onload 的区别

    1.相同点 两者都用于在网页加载完后执行相应代码块. 2.不同点 window.onload 在创建完 DOM 树后,所有外部资源(图片.Flash 动画等)加载完成,且整个页面在浏览器窗口中显示完毕 ...

  6. Laravel 5.2 四、.env 文件与模型操作

    一..env文件 .env 文件是应用的环境配置文件,在配置应用参数.数据库连接.缓存处理时都会使用这个文件. // 应用相关参数 APP_ENV=local APP_DEBUG=true //应用调 ...

  7. python基础===getattr()函数使用方法

    getattr(object, name[,default]) 获取对象object的属性或者方法,如果存在打印出来,如果不存在,打印出默认值,默认值可选.需要注意的是,如果是返回的对象的方法,返回的 ...

  8. nfs 文件共享 服务

    需要rpc服务: [root@xujiaxuan ftp]# service rpcbind start[root@xujiaxuan ftp]# chkconfig rpcbind on 设置开机自 ...

  9. AMD嵌入式G系列SoC协助优化Gizmo 2开发板

    http://www.gizmosphere.org/ AMD嵌入式G系列SoC协助优化Gizmo 2开发板 http://news.zol.com.cn/491/4910444.html

  10. ASP.NET 163 smtp服务器响应为:User has no permission

    1.问题引出 今天在asp.net程序中,利用System.Net.Mail.MailMessage类和网易163免费邮箱服务器发送邮件时出现了如下问题. 2.解决方案 原因很简单,我们在asp.ne ...