[JLOI2015]城池攻占 左偏树
下面讲做法: 首先我们可以观察到每个骑士都是独立的,因此对于每个城池我们可以建一个堆,堆中维护的是在这个城池的骑士。
维护一个小根堆,所以如果堆顶的骑士攻击力不够的话,就可以直接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 }