【题目大意】

有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. js_实现给未来元素添加事件。

    未来元素:不是一个页面上的元素,是通过js或者通过后台直接渲染在页面上的元素,也就是说这些元素不是直接写在document中的. 1.对于未来元素,我们想直接用js或者jq操作它们是不起作用的. $( ...

  2. Android日历开发之右上角标注红点事件

    1.效果如下所示: 2.方法:      前提:已经知道如何在右上角画圆点的情况下.      这是一个任务显示器,每个任务都有一个时间,比如2014.01.12.      如果要标注2016.08 ...

  3. linux编程之多线程编程

    我们知道,进程在各自独立的地址空间中运行,进程之间共享数据需要用mmap或者进程间通信机制,有些情况需要在一个进程中同时执行多个控制流程,这时候线程就派上了用场,比如实现一个图形界面的下载软件,一方面 ...

  4. Bit banging

    Bit banging Bit banging is a technique for serial communications using software instead of dedicated ...

  5. 压缩LDF档

    --压缩LDF档 USE VoucherServer; GO -- Truncate the log by changing the database recovery model to SIMPLE ...

  6. centos7下opencv的安装

    os:centos7 opencv:opencv3.0.0 for linux reference:http://www.cnblogs.com/xixixing/p/6096057.html det ...

  7. 2015多校第6场 HDU 5353 Average 贪心,细节处理

    题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=5353 题意:有n个人围城一个环,每一个人手里都有一些糖果,第i个人有ai块.现在有三种操作:第i个人给 ...

  8. Makefile系列之三 : 变量

    一.变量的基础 变量在声明时需要给予初值,而在使用时,需要给在变量名前加上“$”符号,但最好用小括号“()”或是大括号“{}”把变量给包括起来.如果你要使用真实的“$”字符,那么你需要用“$$”来表示 ...

  9. Django-form組件補充

    自定义验证规则 方法一: 1 2 3 4 5 6 7 8 9 10 from django.forms import Form   from django.forms import widgets f ...

  10. hdu 2955(概率转化,01背包)

    Hot~~招聘——巴卡斯(杭州),壹晨仟阳(杭州),英雄互娱(杭州) (包括2016级新生)除了校赛,还有什么途径可以申请加入ACM校队? Robberies Time Limit: 2000/100 ...