Legend
给定 (n) 个结点的一棵内向树,每条向上的边有一个转换器,可以使得经过的数字发生下列两种变化之一:
- 加上 (v_i)。
- 乘以 (v_i (v_i > 0))。
现在有 (m) 个数字从一些点出发向根走,走过转换器就会发生变化。
同时每个点有一个限制 (h_i),小于 (h_i) 的数字走到这里就不能再移动。特别地,走到了根以后再走会跳出这棵树。
请计算,每一个数字走过了多少条边,以及每一个点停下了多少个数字。
(1 le n,m le 3 imes 10^5),任何时刻,数字的绝对值都在 (10^{18}) 内。
Editorial
考虑一步一步从下往上合并数字集合,对于每一个结点的限制,就直接丢掉不满足的数字(一定是一个前缀)。
由于转换器的操作并不会改变集合数字的相对大小,而且这两个标记是可以像 lazy tag 一样合并的,应该会方便不少。
问题是怎么合并?归并排序吗?
变成 (O(m^2)) 了……
堆启发式合并吗?
变成 (O(m log^2 m)) 了……
好吧,其实可以直接用左偏树,并且把左偏树也加上 lazy tag 就可以啦!
复杂度 (O(m log m))。
Code
要注意的地方是要时时刻刻 pushdown 懒标记。
以及调用左偏树一定要调用它的根,不然就会 MLE 很惨。。。
#include <bits/stdc++.h>
#define debug(...) ;//fprintf(stderr ,__VA_ARGS__)
#define __FILE(x)
freopen(#x".in" ,"r" ,stdin);
freopen(#x".out" ,"w" ,stdout)
#define LL long long
const int MX = 3e5 + 23;
const LL MOD = 998244353;
LL read(){
char k = getchar(); LL x = 0 ,flg = 1;
while(k < '0' || k > '9') flg *= k == '-' ? -1 : 1 ,k = getchar();
while(k >= '0' && k <= '9') x = x * 10 + k - '0' ,k = getchar();
return x * flg;
}
int head[MX] ,tot;
struct edge{
int node ,next;
}h[MX];
void addedge(int u ,int v){
h[++tot] = (edge){v ,head[u]} ,head[u] = tot;
}
#define lch tr[x].ch[0]
#define rch tr[x].ch[1]
struct LeftTree{
int dis ,rt ,ch[2];
LL v ,add ,mul;
LeftTree(){
dis = rt = ch[0] = ch[1] = v = 0;
add = 0 ,mul = 1;
}
}tr[MX];
int find(int x){return tr[x].rt == x ? x : tr[x].rt = find(tr[x].rt);}
void domul(int x ,LL v){
tr[x].add *= v;
tr[x].mul *= v;
tr[x].v *= v;
}
void doadd(int x ,LL v){
tr[x].add += v;
tr[x].v += v;
}
void pushdown(int x){
if(tr[x].mul != 1){
if(lch) domul(lch ,tr[x].mul);
if(rch) domul(rch ,tr[x].mul);
tr[x].mul = 1;
}
if(tr[x].add){
if(lch) doadd(lch ,tr[x].add);
if(rch) doadd(rch ,tr[x].add);
tr[x].add = 0;
}
}
int merge(int x ,int y){
debug("Merge %d %d
" ,x ,y);
if(!x || !y) return x + y;
pushdown(x) ,pushdown(y);
if(tr[x].v > tr[y].v) std::swap(x ,y);
rch = merge(rch ,y);
if(tr[lch].dis < tr[rch].dis) std::swap(lch ,rch);
tr[x].dis = tr[rch].dis + 1;
tr[lch].rt = tr[rch].rt = tr[x].rt = x;
return x;
}
void pop(int x){
pushdown(x);
tr[x].v = LLONG_MAX;
tr[lch].rt = lch;
tr[rch].rt = rch;
tr[x].rt = merge(lch ,rch);
}
int n ,m;
LL def[MX] ,del[MX];
int type[MX];
int castle[MX] ,knight[MX];
int st[MX] ,dep[MX];
LL a[MX];
int refer[MX];
void dfs(int x){
for(int i = head[x] ,d ; i ; i = h[i].next){
dep[d = h[i].node] = dep[x] + 1;
dfs(d);
if(refer[x]){
refer[x] = merge(find(refer[x]) ,find(refer[d]));
}
else{
refer[x] = find(refer[d]);
}
}
while(tr[find(refer[x])].v < def[x]){
int id = find(refer[x]);
debug("knight %d die on castle %d!
" ,id ,x);
knight[id] = dep[st[id]] - dep[x];
++castle[x];
pop(id);
}
if(!find(refer[x])) return;
if(type[x] == 0){
doadd(find(refer[x]) ,del[x]);
}
else{
domul(find(refer[x]) ,del[x]);
}
// debug("v[0] = %lld after dfs %d
" ,tr[0].v ,x);
}
void solve(){
tr[0].v = LLONG_MAX;
tr[0].dis = -1;
n = read() ,m = read();
for(int i = 1 ; i <= n ; ++i){
def[i] = read();
}
for(int i = 2 ,f ; i <= n ; ++i){
f = read() ,type[i] = read() ,del[i] = read();
addedge(f ,i);
}
for(int i = 1 ; i <= m ; ++i){
a[i] = read() ,st[i] = read();
tr[i].v = a[i] ,tr[i].rt = i;
tr[i].ch[0] = tr[i].ch[1] = 0;
if(!refer[st[i]]){
refer[st[i]] = i;
}
else{
merge(find(refer[st[i]]) ,find(i));
}
}
dfs(1);
while(tr[find(refer[1])].v != LLONG_MAX){
int id = find(refer[1]);
knight[id] = dep[st[id]] + 1;
pop(id);
}
for(int i = 1 ; i <= n ; ++i){
printf("%d
" ,castle[i]);
}
for(int j = 1 ; j <= m ; ++j){
printf("%d
" ,knight[j]);
}
}
int main(){
__FILE([JLOI2015]城池攻占);
int T = 1;
for(int i = 1 ; i <= T ; ++i){
solve();
}
return 0;
}