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 */

 

posted @ 2022-08-10 12:01  YHXo  阅读(33)  评论(0)    收藏  举报