【左偏树+延迟标记+拓扑排序】BZOJ4003-城池攻占

【题目大意】

有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中将较小根的右子树和较大根的左子树合并的时候。(!!!)

  1 #include<iostream>
  2 #include<cstdio>
  3 #include<cstring>
  4 #include<algorithm>
  5 #include<vector>
  6 #include<queue>
  7 #define L Ltree[Ltree[x].lson]
  8 #define R Ltree[Ltree[x].rson]
  9 using namespace std;
 10 typedef long long ll;
 11 const int MAXN=300000+50;
 12 struct node
 13 {
 14     ll key;
 15     int dis,pos,cnt;
 16     ll lazyadd,lazymul;int lazycnt;//懒惰标记
 17     int lson,rson;
 18 };
 19 int n,m;
 20 ll h[MAXN],v[MAXN];//防御力,战斗变化量,
 21 int city[MAXN],knight[MAXN],f[MAXN],a[MAXN],out[MAXN],rt[MAXN];
 22 //每个城市牺牲的骑士数,每个骑士攻破的城市数量,管辖地,战斗变化参数,每个节点的出度(拓扑排序使用),到达每个节点位置时的堆顶元素
 23 node Ltree[MAXN];//左偏树
 24 queue<int> que;//树由下至上拓扑排序的队列
 25
 26 void update(int root,int flag,ll delta)
 27 {
 28     Ltree[root].lazycnt++;
 29     Ltree[root].cnt++;
 30     if (flag)
 31     {
 32         Ltree[root].lazyadd*=delta;
 33         Ltree[root].lazymul*=delta;
 34         Ltree[root].key*=delta;
 35     }
 36     else
 37     {
 38         Ltree[root].lazyadd+=delta;
 39         Ltree[root].key+=delta;
 40     }
 41 }
 42
 43 void pushdown(int x)
 44 {
 45     if (Ltree[x].lazycnt)
 46     {
 47         L.cnt+=Ltree[x].lazycnt;
 48         R.cnt+=Ltree[x].lazycnt;
 49         L.lazycnt+=Ltree[x].lazycnt;
 50         R.lazycnt+=Ltree[x].lazycnt;
 51         Ltree[x].lazycnt=0;
 52     }
 53     if (Ltree[x].lazymul!=1)
 54     {
 55         L.key*=Ltree[x].lazymul;
 56         R.key*=Ltree[x].lazymul;
 57         L.lazyadd*=Ltree[x].lazymul;
 58         R.lazyadd*=Ltree[x].lazymul;
 59         L.lazymul*=Ltree[x].lazymul;
 60         R.lazymul*=Ltree[x].lazymul;
 61         Ltree[x].lazymul=1;
 62     }
 63     if (Ltree[x].lazyadd)
 64     {
 65         L.key+=Ltree[x].lazyadd;
 66         R.key+=Ltree[x].lazyadd;
 67         L.lazyadd+=Ltree[x].lazyadd;
 68         R.lazyadd+=Ltree[x].lazyadd;
 69         Ltree[x].lazyadd=0;
 70     }
 71 }
 72
 73 int merge(int x,int y)
 74 {
 75     if (!x||!y)
 76     {
 77         return(x+y);
 78     }
 79     if (Ltree[x].key>Ltree[y].key) swap(x,y);
 80     pushdown(x);
 81     //!!!这里要pushdown!!这里千万不要忘记pushdown!
 82     Ltree[x].rson=merge(Ltree[x].rson,y);
 83     int &l=Ltree[x].lson,&r=Ltree[x].rson;
 84     if (Ltree[l].dis<Ltree[r].dis) swap(l,r);
 85     if (r==0) Ltree[x].dis=0;
 86         else Ltree[x].dis=Ltree[r].dis+1;
 87     return x;
 88 }
 89
 90 void init()
 91 {
 92     scanf("%d%d",&n,&m);
 93     memset(rt,0,sizeof(rt));
 94     for (int i=1;i<=n;i++) scanf("%lld",&h[i]);
 95     for (int i=2;i<=n;i++)
 96     {
 97         scanf("%d%d%lld",&f[i],&a[i],&v[i]);
 98         out[f[i]]++;
 99     }
100     Ltree[0].dis=-1;
101     for (int i=1;i<=m;i++)
102     {
103         ll s;int c;
104         scanf("%lld%d",&s,&c);
105         Ltree[i]=(node){s,0,i,0,0,1,0};
106         rt[c]=merge(rt[c],i);
107     }
108 }
109
110 void Topology()
111 {
112     queue<int> que;
113     for (int i=1;i<=n;i++) if (!out[i]) que.push(i);
114     while (!que.empty())
115     {
116         int u=que.front();que.pop();
117         int& root=rt[u];
118         int father=f[u];
119         while (root && (h[u]>Ltree[root].key))//如果堆顶元素小于城市的防御力,即该骑士会牺牲,则不断弹出
120         {
121             knight[Ltree[root].pos]=Ltree[root].cnt;
122             city[u]++;
123             pushdown(root);
124             root=merge(Ltree[root].lson,Ltree[root].rson);
125         }
126         update(root,a[u],v[u]);
127         rt[father]=merge(rt[father],root);
128         out[father]--;
129         if (!out[father]) que.push(father);
130     }
131
132     while (rt[1])//处理所有能够抵达根节点的所有骑士
133     {
134         knight[rt[1]]=Ltree[rt[1]].cnt;
135         pushdown(rt[1]);
136         rt[1]=merge(Ltree[rt[1]].lson,Ltree[rt[1]].rson);
137     }
138 }
139
140 void printans()
141 {
142     for (int i=1;i<=n;i++) printf("%d\n",city[i]);
143     for (int j=1;j<=m;j++) printf("%d\n",knight[j]);
144 }
145
146 int main()
147 {
148     init();
149     Topology();
150     printans();
151     return 0;
152 }
时间: 2024-08-29 23:21:51

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

左偏树初步 bzoj2809 &amp; bzoj4003

看着百度文库学习了一个. 总的来说,左偏树这个可并堆满足 堆的性质 和 左偏 性质. bzoj2809: [Apio2012]dispatching 把每个忍者先放到节点上,然后从下往上合并,假设到了这个点 总值 大于 预算,那么我们把这个 大根堆 的堆顶弹掉就好了,剩下的就是可合并堆. 感谢prey :) 1 #include <bits/stdc++.h> 2 #define rep(i, a, b) for (int i = a; i <= b; i++) 3 #define dr

左偏树(BZOJ4003)

左偏树打个标记,没了. #include <cstdio> #include <vector> using namespace std; typedef long long ll; const int N = 300005; int n,m,e,x,tt,c[N],d[N],a[N],hd[N],nxt[N],to[N],rt[N],f[N],g[N]; ll h[N],v[N],s[N]; vector<int> vc[N]; struct nd {int l,r,d

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

[题目大意] 给定一个序列t1,t2,...,tn ,求一个递增序列z1<z2<...<zn , 使得R=|t1−z1|+|t2−z2|+...+|tn−zn| 的值最小.本题中,我们只需要求出这个最小的R值. [思路] -这个比加延迟标记的左偏树调试得还久……WA到死…… 如果ti是递增的,我们只需要取zi=ti: 如果ti是递减的,我们只需要取ti的中位数. 所以我们将ti分割成若干个区间,维护每个区间的中位数.对于[L,R]的区间,我们存放[L,(L+R)/2]在堆中.具体如下操作

【BZOJ2333】棘手的操作(左偏树,STL)

[BZOJ2333]棘手的操作(左偏树,STL) 题面 BZOJ上看把... 题解 正如这题的题号 我只能\(2333\) 神TM棘手的题目... 前面的单点/联通块操作 很显然是一个左偏树+标记 (确实很显然,只是写死人...) 然后对于全局的最大值而言 搞一个\(multi\)来水 看起来真的简单.. 写起来真的想死... 记住:要特判一下已经联通的块就不要再去\(Merge\)了 #include<iostream> #include<cstdio> #include<

[洛谷P3261] [JLOI2015]城池攻占(左偏树)

不得不说,这道题目是真的难,真不愧它的"省选/NOI-"的紫色大火题!!! 花了我晚自习前半节课看题解,写代码,又花了我半节晚自习调代码,真的心态爆炸.基本上改得和题解完全一样了我才过了这道题!真的烦.没事,那接下来我来完全把这道题搞透. Part 1 理解题目 至少我一开始不知道为什么要用左偏树,甚至我看题解一开始也都没弄懂,所以先把题目弄清楚. 首先我们由题可以知道,这要求我们从建好的树的叶子节点开始往上推,有些骑士到特定的点才会出现,check一下骑士能否攻占城池,再记录进答案,

P3377 【模板】左偏树(可并堆) 左偏树浅谈

因为也是昨天刚接触左偏树,从头理解,如有不慎之处,跪请指教. 左偏树: 什 么是(fzy说)左偏树啊? 前置知识: 左偏树中dist:表示到右叶点(就是一直往右下找,最后一个)的距离,特别的,无右节点的为0. 堆:左偏树是个堆. 关于左偏性质:可以帮助堆合并(研究深了我也不懂的,看代码理解) 对于任意的节点,dist[leftson]>=dist[rightson],体现了左偏性质. 同理可得:对于任意右儿子的父亲节点的dist自然等于右儿子的dist+1喽 关于各种操作: merge: 是插入

P3273 [SCOI2011]棘手的操作 左偏树

一道恶心至极的左偏树题 题意: 题解: 观察操作可以发现需要用两棵左偏树来维护 一棵维护全局最大值  一棵维护正常 操作一:普通左偏树正常连边 全局左偏树可以删去小的那个点 操作二:普通左偏树正常操作(删点-增值-merge)全局左偏树先删去x点连通块祖先 然后再加回连通块祖先  这样做比对全局左偏树也正常操作有优越性  点会越来越少  如果全局左偏树也和局部左偏树一样操作会MLE 操作三:和操作二非常相似  普通左偏树的根部加一个值和懒标记即可 全局左偏树类似操作二 #include<bits

关于左偏树的一些东东

大概所有的预备知识这里都有https://baike.baidu.com/item/%E5%B7%A6%E5%81%8F%E6%A0%91/2181887?fr=aladdin 例题1:洛谷 P3377 [模板]左偏树(可并堆) 383通过 1.2K提交 题目提供者HansBug 站长团 标签 难度提高+/省选- 时空限制1s / 128MB 提交 讨论 题解 最新讨论更多讨论 加了路径压缩就WA,路过dal… 左偏树用指针写会MLE吗..… m,n写反了也可以过,数据有… 哪位大神有pbds库

左偏树

概要:左偏树是具有左偏性质的堆有序二叉树,它相比于优先队列,能够实现合并堆的功能. 先仪式型orzorzozr国家集训队论文https://wenku.baidu.com/view/515f76e90975f46527d3e1d5.html 左偏树的节点定义: 1 struct node { 2 int lc, rc, val, dis; 3 } LTree[maxn]; 左偏树的几个基本性质如下: 节点的键值小于等于它的左右子节点的键值 节点的左子节点的距离不小于右子节点的距离 节点的距离等于