P3261 [JLOI2015]城池攻占 (左偏树+标记下传)
左偏树还是满足堆的性质,节点距离就是离最近的外节点(无左或者右儿子 或者二者都没有)的距离,左偏性质就是一个节点左儿子的距离不小于右儿子,由此得:节点距离等于右儿子的距离+1。
本题就是对于每个节点都建立一颗左偏树(小根堆),存的是在当前节点的骑士,从下往上模拟题意就行了。
1 #include <bits/stdc++.h> 2 using namespace std; 3 #define ll long long 4 const int N = 3e5 + 10; 5 int n, m; 6 int fa[N], c[N], a[N], rt[N]; 7 ll h[N], v[N], s[N]; 8 int ls[N], rs[N], dep[N]; 9 int Dep[N], die[N], ans[N]; 10 ll add[N], tim[N];//+ * 11 12 void pushdown(int x) {//下传标记 13 if (add[x] == 0 && tim[x] == 1) return; 14 if (ls[x]) { 15 tim[ls[x]] *= tim[x]; 16 add[ls[x]] *= tim[x]; 17 add[ls[x]] += add[x]; 18 s[ls[x]] *= tim[x]; 19 s[ls[x]] += add[x]; 20 }//先处理乘再处理加 21 if (rs[x]) { 22 tim[rs[x]] *= tim[x]; 23 add[rs[x]] *= tim[x]; 24 add[rs[x]] += add[x]; 25 s[rs[x]] *= tim[x]; 26 s[rs[x]] += add[x]; 27 } 28 add[x] = 0, tim[x] = 1; //恢复标记 29 } 30 31 int merge(int x, int y) { 32 if (!x || !y) return x + y; 33 pushdown(x), pushdown(y); 34 if (s[x] > s[y]) swap(x, y);//小根堆 35 rs[x] = merge(rs[x], y);//x的右节点与y合并 36 if (dep[ls[x]] < dep[rs[x]]) swap(ls[x], rs[x]);//维护左偏性质 37 dep[x] = dep[rs[x]] + 1; 38 return x; 39 } 40 41 int main() { 42 scanf("%d %d", &n, &m); 43 for (int i = 1; i <= n; i++) { 44 scanf("%lld", &h[i]);//城池防御力 45 rt[i] = -1;//设为空 46 } 47 Dep[1] = 1, dep[0] = -1; 48 for (int i = 2; i <= n; i++) { 49 scanf("%d %d %lld", &fa[i], &a[i], &v[i]);//父亲 城池能力改变方式 城池能力改变值 50 Dep[i] = Dep[fa[i]] + 1; 51 } 52 for (int i = 1; i <= m; i++) { 53 scanf("%lld %d", &s[i], &c[i]);//骑士能力 出生地 54 tim[i] = 1; 55 if (rt[c[i]] == -1) rt[c[i]] = i; 56 else rt[c[i]] = merge(rt[c[i]], i);//合并同城骑士 57 } 58 for (int i = n; i >= 1; i--) {//从下到上 59 while (rt[i] != -1) {//当前堆不为空 60 if (s[rt[i]] < h[i]) { 61 die[rt[i]] = i;//死亡 62 pushdown(rt[i]); 63 if (!ls[rt[i]]) rt[i] = -1; 64 else rt[i] = merge(ls[rt[i]], rs[rt[i]]); 65 } 66 else break;//剩下的不死 67 } 68 if (i == 1) break;//特判根节点 69 if (rt[i] == -1) continue; 70 if (a[i]) tim[rt[i]] *= v[i], add[rt[i]] *= v[i], s[rt[i]] *= v[i]; 71 else add[rt[i]] += v[i], s[rt[i]] += v[i]; 72 pushdown(rt[i]); 73 if (rt[fa[i]] == -1) rt[fa[i]] = rt[i]; 74 else rt[fa[i]] = merge(rt[fa[i]], rt[i]);//幸存骑士到父节点 75 } 76 for (int i = 1; i <= m; i++) ans[die[i]]++; 77 for (int i = 1; i <= n; i++) printf("%d\n", ans[i]); 78 for (int i = 1; i <= m; i++) printf("%d\n", Dep[c[i]] - Dep[die[i]]); 79 return 0; 80 } 81 /* 82 1.注意tim 初始化为1 83 2.dep[0] = -1 ***** 左偏树中空节点距离要设为-1 84 3.注意处理标记 85 4.特判骑士死光的情况(MLE) 86 */

浙公网安备 33010602011771号