【左偏树+延迟标记+拓扑排序】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 }