[JLOI2015]城池攻占 左偏树

表示调这道题已经调到失智了。。。 因为昨天刚写完线段树2,所以pushdown就写得和线段树2一模一样,,,,于是,,,成功的在pushdown的地方,,,各种错

下面讲做法: 首先我们可以观察到每个骑士都是独立的,因此对于每个城池我们可以建一个堆,堆中维护的是在这个城池的骑士。

维护一个小根堆,所以如果堆顶的骑士攻击力不够的话,就可以直接pop掉,并且给城池死亡人数统计++(dead),这样用while pop完后,堆里的骑士就都是可以占领这个城池的了,

由于下面的骑士可以上来,所以这个堆要支持合并,所以我们用左偏树

因为pop完后剩下的都是合法的,所以这个时候我们就可以对剩下的骑士进行修改了。但是我们观察到,骑士很多,城池也很多,直接修改显然不太妥当。于是我们借鉴线段树的lazy标记,但是由于这里有乘法也有加法,所以我们维护两个标记,一个mul(乘法标记),一个lazy(加法标记),

由于(x+h)q=xq+hq,所以我们可以把h当做lazy,x当做当前值,q是mul,所以我们更改和下传乘法标记的时候,要同时把lazy也=mul,

这时lazy==hq,mul==q,因此我们要得到xq+h*q这个结果,我们需要先乘后加

deadin[i]代表骑士i死在哪里了,dead代表死在这个城池的人数,keep是城市的生命值,其他数组应该都好懂了

因为我们观察到如果还要下传一个标记won来标记骑士又占领了一个城池的话显然是不划算的,因为mul和lazy之所以有必要就是因为过程中要用到,而won标记仅仅是最后要用而已,并且由于骑士的前进路径是一条链,所以我们直接用死亡城市深度-出发城市深度就可以得到攻占几座城了,如果没死的话死亡城市就是0,因此为了维护死亡城市-出发城市这个式子的正确性,我们的deep统计从1开始

如果还有不懂的就看代码吧,还是有少量注释的

推荐写之前先写线段树2(luogu3373),有利于理解如何同时维护乘法和加法标记,这里的标记本质上是一样的,但是注意不要像我一样,写完线段树2,pushdown就全写成线段树版本的,,,,于是就各种错,,,

下面的输出格式是为了方便我调试改的,所以。。。。

写的可能比较复杂???

表示并不知道为什么我的代码一直比别人长。。。。

-------2018.10.11-------优化了代码格式

  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 #define R register int
  4 #define AC 300050
  5 #define LL long long
  6 #define D printf("line in %d\n",__LINE__);
  7 int n, m;
  8 int date[AC], Next[AC], Head[AC], tot, cnt;//前向星
  9 LL v[AC], keep[AC];
 10 int deep[AC], a[AC], dead[AC], deadin[AC];//城池属性, 答案
 11 int b[AC], root[AC];
 12 
 13 inline LL read()
 14 {
 15     LL x = 0; char c = getchar(); bool z = false;
 16     while(c > '9' || c < '0') 
 17     {
 18         if(c == '-') z = true;
 19         c = getchar();
 20     }
 21     while(c >= '0' && c <= '9') x = x * 10 + c - '0', c = getchar();
 22     if(!z) return x;
 23     else return -x;
 24 }
 25 
 26 inline void add(int f, int w)
 27 {
 28     date[++tot] = w, Next[tot] = Head[f], Head[f] = tot;
 29 }
 30 
 31 struct left_heap
 32 {
 33     LL s[AC], mul[AC], lazy[AC];//骑士属性和标记,error!!!标记也要LL啊啊啊啊啊啊啊啊,因为标记也可能乘到很大啊,比如lazy和mul各种乘之类的
 34     int l[AC], r[AC], num[AC];
 35 
 36     inline void pushdown(int x)
 37     {
 38         int ll = l[x], rr = r[x];//error!!!这又不是线段树啊!!!!干嘛乘2啊 
 39         if(!mul[x]) return ;//如果mul为0,说明没有这个堆,所以
 40         if(mul[x] != 1)
 41         {
 42             s[ll] *= mul[x], mul[ll] *= mul[x], lazy[ll] *= mul[x];//error!!!加法标记也要乘啊!!!
 43             s[rr] *= mul[x], mul[rr] *= mul[x], lazy[rr] *= mul[x];
 44             mul[x] = 1;
 45         }
 46         if(lazy[x])
 47         {
 48             s[ll] += lazy[x], lazy[ll] += lazy[x];
 49             s[rr] += lazy[x], lazy[rr] += lazy[x];
 50             lazy[x]=0;//error!!!不是线段树啊,,,,r,l不是区间了
 51         }
 52     }
 53 
 54     inline void change_add(int x, int k)
 55     {
 56         if(!x) return ;
 57         s[x] += k, lazy[x] += k;
 58     }
 59 
 60     inline void change_mul(int x, int k)
 61     {
 62         if(!x) return ;
 63         s[x] *= k, lazy[x]*=k, mul[x]*=k;
 64     }
 65 
 66     int merge(int x, int y)
 67     {
 68         pushdown(x), pushdown(y);
 69         if(!x || !y) return x + y;
 70     //  pushdown(x),pushdown(y);
 71         if(s[x] > s[y]) swap(x, y);
 72         r[x] = merge(r[x], y);
 73         swap(l[x], r[x]);
 74         return x;
 75     }
 76 
 77     void pop(int &x)
 78     {
 79         x=merge(l[x], r[x]);
 80     }
 81 
 82     inline void insert(int x, int k)
 83     {
 84         s[++cnt] = x, num[cnt] = k, mul[cnt] = 1;
 85         root[b[k]] = merge(cnt, root[b[k]]);
 86     }
 87 }heap;
 88 
 89 void DFS(int x)
 90 {
 91 /*  if(a[x]) heap.change_mul(root[x],v[x]);
 92     else heap.change_add(root[x],v[x]);
 93     if(!Head[x]) return ;//如果到叶节点就返回,因为没有儿子给它了
 94     */
 95     R now;
 96     for(R i = Head[x]; i; i = Next[i])
 97     {
 98 
 99         now = date[i], deep[now] = deep[x] + 1;
100         DFS(now);
101         root[x] = heap.merge(root[now], root[x]);
102     }
103     while(root[x] && heap.s[root[x]] < keep[x])
104     {
105         dead[x] ++;
106         deadin[heap.num[root[x]]] = x;
107         heap.pop(root[x]);
108     }
109     if(a[x]) heap.change_mul(root[x], v[x]);
110     else heap.change_add(root[x], v[x]);
111 }
112 
113 void pre()
114 {
115     int aa;
116     n = read(), m = read();
117     deep[1] = 1;
118     for(R i = 1; i <= n; i ++) keep[i] = read(), heap.mul[i] = 1;
119     for(R i = 2; i <= n; i ++)
120     {
121         aa = read(), a[i] = read(), v[i] = read();
122         add(aa, i);
123     }
124     for(R i = 1; i <= m; i ++)
125     {
126         aa = read(), b[i] = read();
127     //  printf("%d %d\n",aa,b[i]);
128         if(aa >= keep[b[i]])//如果第一座城池打得下才建堆
129         {
130             if(!root[b[i]])
131             {
132                 root[b[i]] = ++cnt;//应该要给城池开堆,堆里存士兵,不然士兵在哪个城市有上面区别
133                 heap.num[cnt] = i, heap.s[cnt] = aa, heap.mul[cnt] = 1;//因为是给城池开的堆,所以就要存编号了
134                 //printf("%d\n",i);
135             }
136             else heap.insert(aa, i);
137         }
138         else dead[b[i]] ++, deadin[i] = b[i];//不然就别来了
139     }
140 }
141 
142 void work()
143 {
144     for(R i = 1; i <= n; i ++) printf("%d\n", dead[i]);
145     printf("\n");
146     for(R i = 1; i <= m; i ++)
147         printf("%d\n", deep[b[i]] - deep[deadin[i]]);
148 }
149 
150 int main()
151 {
152     freopen("in.in","r",stdin);
153     pre();
154     DFS(1);
155     work();
156     fclose(stdin);
157     return 0;
158 }
View Code

 

posted @ 2018-04-09 12:33  ww3113306  阅读(186)  评论(0编辑  收藏  举报
知识共享许可协议
本作品采用知识共享署名-非商业性使用-禁止演绎 3.0 未本地化版本许可协议进行许可。