传送门
题目大意:
小铭铭最近获得了一副新的桌游,游戏中需要用 m 个骑士攻占 n 个城池。
这 n 个城池用 1 到 n 的整数表示。除 1 号城池外,城池 i 会受到另一座城池 fi 的管辖,
其中 fi < i。也就是说,所有城池构成了一棵有根树。这 m 个骑士用 1 到 m 的整数表示,其
中第 i 个骑士的初始战斗力为 si,第一个攻击的城池为 ci。
每个城池有一个防御值 hi,如果一个骑士的战斗力大于等于城池的生命值,那么骑士就可
以占领这座城池;否则占领失败,骑士将在这座城池牺牲。占领一个城池以后,骑士的战斗力
将发生变化,然后继续攻击管辖这座城池的城池,直到占领 1 号城池,或牺牲为止。
除 1 号城池外,每个城池 i 会给出一个战斗力变化参数 ai;vi。若 ai =0,攻占城池 i 以后骑士战斗力会增加 vi;若 ai =1,攻占城池 i 以后,战斗力会乘以 vi。注意每个骑士是单独计算的。也就是说一个骑士攻击一座城池,不管结果如何,均不会影响其他骑士攻击这座城池的结果.
现在的问题是, 对于每个城池, 输出有多少个骑士在这里牺牲; 对于每个骑士, 输出他攻占的城池数量
这道题算是可并堆中最难的一道了把.. 注意左偏树也是一颗二叉树, 所以是满足标记下推的, 所以对于乘的和加的打上相应的标记即可, 然后即使下放的时候有两个, 一个是当前点要被删除时需要提前下推一下, 而是进行合并的时候要在左右儿子有新编号的时候下推一下. 还有注意的点就是乘和加标记的先后顺序关系, 线段树中的双标记下推规则就不细说了. 这里的root不能初始化为自己, 因为这个跟自己没有任何关系, 所以root[i] == 0 的就不用进行判断了.. 细节请看代码
AC Code
const int maxn = 3e5 + 5;
int root[maxn];
struct Ltree {
int ls[maxn<<1], rs[maxn<<1]; // 左右儿子的编号
ll val[maxn<<1], mul[maxn<<1], add[maxn<<1];
// 该点的值, 改点的+/*的标记, 因为左偏树也是二叉树
// 所以可以想线段树那样进行下放
void funmul(int id, ll tmp) {
val[id] *= tmp; mul[id] *= tmp;
add[id] *= tmp;
}
void funadd(int id, ll tmp) {
val[id] += tmp; add[id] += tmp;
}
void pushdown(int id) {
if (mul[id] != 1) {
funmul(ls[id], mul[id]);
funmul(rs[id], mul[id]);
mul[id] = 1;
}
if (add[id]) {
funadd(ls[id], add[id]);
funadd(rs[id], add[id]);
add[id] = 0;
}
}
int Un(int x, int y){ // 小根堆/ 看情况维护, 改个符号就行
if (!x || !y) return x+y;
if (val[x] > val[y]) swap(x,y);
pushdown(x);
rs[x] = Un(rs[x], y);
swap(ls[x], rs[x]);
return x;
}
void pop(int &x) {
x = Un(ls[x], rs[x]);
}
int top(int x) {
return val[x];
}
}heap;
int a[maxn];
ll h[maxn], v[maxn], ci[maxn];
int ans[maxn], siz[maxn], dep[maxn];
int n, m;
struct node {
int to, next, w;
}e[maxn];
int cnt, head[maxn];
void init() {
cnt = 0; Fill(head, -1);
}
void add(int u, int v, int w) {
e[cnt] = node{v, head[u], w};
head[u] = cnt++;
}
void dfs(int u, int fa, int d) {
dep[u] = d + 1;
for (int i = head[u] ; ~i ; i = e[i].next) {
int to = e[i].to;
dfs(to, u, d + 1);
root[u] = heap.Un(root[u], root[to]);
}
if (!root[u]) return ;
int rf = root[u];
while (heap.val[rf] < h[u]) {
heap.pushdown(rf);
++ siz[u];
ans[rf] = dep[ci[rf]] - dep[u];
heap.pop(root[u]);
if (!root[u]) return ;
rf = root[u];
}
if (a[u]) heap.funmul(rf, v[u]);
else heap.funadd(rf, v[u]);
}
void solve() {
scanf("%d%d", &n, &m); init();
for (int i = 1 ; i <= n ; i ++) scanf("%lld", h+i);
for (int i = 2 ; i <= n ; i ++) {
int x;
scanf("%d%d%lld", &x, &a[i], &v[i]);
add(x, i, 1);
}
for (int i = 1 ; i <= m ; i ++) {
ll x;
scanf("%lld%d", &x, ci+i);
heap.val[i] = x; heap.mul[i] = 1;
root[ci[i]] = heap.Un(root[ci[i]], i);
}
Fill(ans, -1); dfs(1, -1, 0);
for (int i = 1; i <= n ; i ++) printf("%d\n", siz[i]);
for (int i = 1; i <= m ; i ++) {
printf("%d\n", ans[i] == -1 ? dep[ci[i]]: ans[i]);
}
}