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

 

posted @ 2016-07-20 12:37  iiyiyi  阅读(277)  评论(0编辑  收藏  举报