CF1178G The Awesomest Vertex

分块 + 维护凸壳


题意

给定 nn 个节点根为 11 的有根树,每个节点有两个权值 a,ba, b ,定义 fa(x)fa(x)xx 祖先的集合(包括 xx ),则节点 xx 的贡献为:

vfa(x)avvfa(x)bv| \sum\limits_{v \in fa(x)} a_v | \cdot | \sum\limits_{v \in fa(x)} b_v |

需要支持两种操作:

  • 1 x v1\ x\ v \rightarrowaxa_x 加上 vv
  • 2 x2\ x \rightarrow 求出 xx 为根的子树中的节点的最大贡献

其中 1n2105,1q105,5103ai,bi5103,1xn,1v51031 \leq n \leq 2 \cdot 10^5, 1 \leq q \leq 10^5, -5 \cdot 10^3 \leq a_i, b_i \leq 5 \cdot 10^3, 1 \leq x \leq n, 1 \leq v \leq 5 \cdot 10^3

思路

之前遇到过很多次类似维护 aibia_i \cdot b_i 的情况,一直鸽了。
对于本题,显然可以预处理出每个节点祖先的 ai,bia_i, b_i 的和,将它们的值记为 ai,bia_i, b_i ,操作 1,21, 2 即变为对子树的操作,而对子树的操作可以通过 dfsdfs 序拍成区间操作。
如果只是针对 aia_i 的加减操作,本质上每个节点的贡献值可以表示为一条直线 y=bix+aibiy = b_i \cdot x + a_i \cdot b_ixx 可以理解为每次 aia_i 的增量。对结果取绝对值后相当于两条直线 y=bix+aibiy = b_i \cdot x + a_i \cdot b_iy=bixaibiy = - b_i \cdot x - a_i \cdot b_i 取最大值。
那么可以考虑对于树的 dfndfn 序列分块,每个块维护一个下凸壳,表示这个块内的所有节点代表的直线(正负 22 条)所形成的凸壳。
对于操作 11 ,对于被包含在区间内的块,直接维护一个标记记录这个块内 aia_i 的增量,对于边缘的块,直接暴力重构凸壳即可。单次复杂度为 O(n)O(\sqrt{n})
对于操作 22 ,对于被包含在区间内的块,直接返回凸壳上对应增量的值,对于边缘的块,直接暴力计算对应 aia_i 加上块标记的值即可。其中对于每个凸壳额外维护一个值 tagxtagx 表示该块内对答案产生贡献的直线的下标,由于 v[1,5103]v \in [1, 5 \cdot 10^3]tagxtagx 的变化是单调的,查询时直接维护即可。单次复杂度为 O(n)O(\sqrt{n})
nn 次操作的总复杂度为 O(nn)O(n \cdot \sqrt{n})

代码

注意重构凸壳时判断直线交点可能会爆 long longlong\ longint128int128 即可。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
/*
Author: Cupids_Bow
Time: 2022-08-11 23:15:36
*/
#include<bits/stdc++.h>
using namespace std;

using ll=long long;

constexpr int N=2e5+5;

int n,q;
ll a[N],b[N];
vector<int> e[N];
int dfn[N],dfn0,fi[N],lst[N];
int blo,bl[N];
ll lz[N],tagx[N];
struct Line{
ll k;
ll b;
Line(){}
Line(ll _k,ll _b){
k=_k;
b=_b;
}
ll calc(ll x){
return k*x+b;
}
};
vector<Line> convex[N];

void dfs(int x,int fa){
dfn[x]=++dfn0;
fi[x]=dfn0;
for(auto v:e[x]){
if(v==fa) continue;
a[v]+=a[x];
b[v]+=b[x];
dfs(v,x);
}
lst[x]=dfn0;
}

int is_bad(Line &l1,Line &l2,Line &l3){
return (__int128_t)(l2.b-l3.b)*(l2.k-l1.k)<=(__int128_t)(l1.b-l2.b)*(l3.k-l2.k);
}

void build(int id,vector<Line> &vec){
convex[id].clear();
sort(vec.begin(),vec.end(),[&](const Line &l1,const Line &l2){
if(l1.k!=l2.k) return l1.k<l2.k;
else return l1.b<l2.b;
});
for(auto line:vec){
int sz=convex[id].size();
while(sz>=2&&is_bad(convex[id][sz-2],convex[id][sz-1],line)) convex[id].pop_back(),sz--;
convex[id].push_back(line);
}
lz[id]=tagx[id]=0;
}

void init(){
vector<ll> veca(n+5),vecb(n+5);
blo=max(1.0,sqrt(n/6));
for(int i=1;i<=n;i++){
bl[i]=(i-1)/blo+1;
veca[dfn[i]]=a[i];
vecb[dfn[i]]=b[i];
}
for(int i=1;i<=n;i++) a[i]=veca[i],b[i]=vecb[i];
for(int l=1;l<=n;){
vector<Line> vec;
vec.push_back(Line(b[l],a[l]*b[l]));
vec.push_back(Line(-b[l],-a[l]*b[l]));
int r=l,id=bl[l];
while(r+1<=n&&bl[r+1]==id){
r++;
vec.push_back(Line(b[r],a[r]*b[r]));
vec.push_back(Line(-b[r],-a[r]*b[r]));
}
build(id,vec);
l=r+1;
}
}

void add(int ql,int qr,ll v){
if(bl[ql]==bl[qr]){
int id=bl[ql];
for(int i=ql;i<=qr;i++) a[i]+=v;
while(ql-1>=1&&bl[ql-1]==id) ql--;
while(qr+1<=n&&bl[qr+1]==id) qr++;
vector<Line> vec;
for(int i=ql;i<=qr;i++){
a[i]+=lz[id];
vec.push_back(Line(b[i],a[i]*b[i]));
vec.push_back(Line(-b[i],-a[i]*b[i]));
}
build(id,vec);
return;
}
int L=bl[ql]+1,R=bl[qr]-1;
for(int i=L;i<=R;i++) lz[i]+=v;
int id=bl[ql];
vector<Line> vec;
for(int i=ql;bl[i]==id;i++) a[i]+=v;
while(ql-1>=1&&bl[ql-1]==id) ql--;
for(int i=ql;bl[i]==id;i++){
a[i]+=lz[id];
vec.push_back(Line(b[i],a[i]*b[i]));
vec.push_back(Line(-b[i],-a[i]*b[i]));
}
build(id,vec);
id=bl[qr],vec.clear();
for(int i=qr;bl[i]==id;i--) a[i]+=v;
while(qr+1<=n&&bl[qr+1]==id) qr++;
for(int i=qr;bl[i]==id;i--){
a[i]+=lz[id];
vec.push_back(Line(b[i],a[i]*b[i]));
vec.push_back(Line(-b[i],-a[i]*b[i]));
}
build(id,vec);
return;
}

ll find(int id){
while(tagx[id]+1<convex[id].size()&&convex[id][tagx[id]].calc(lz[id])<=convex[id][tagx[id]+1].calc(lz[id])) tagx[id]++;
return convex[id][tagx[id]].calc(lz[id]);
}

ll search(int ql,int qr){
ll res=-1e18;
if(bl[ql]==bl[qr]){
int id=bl[ql];
for(int i=ql;i<=qr;i++) res=max(res,abs(a[i]+lz[id])*b[i]);
return res;
}
int L=bl[ql]+1,R=bl[qr]-1;
for(int i=L;i<=R;i++) res=max(res,find(i));
for(int i=ql;bl[i]==bl[ql];i++) res=max(res,abs(a[i]+lz[bl[ql]])*b[i]);
for(int i=qr;bl[i]==bl[qr];i--) res=max(res,abs(a[i]+lz[bl[qr]])*b[i]);
return res;
}

int main(){
ios::sync_with_stdio(0);
cin.tie(0);cout.tie(0);

cin>>n>>q;
for(int i=2,x;i<=n;i++){
cin>>x;
e[x].push_back(i);
e[i].push_back(x);
}
for(int i=1;i<=n;i++) cin>>a[i];
for(int i=1;i<=n;i++) cin>>b[i];
dfs(1,1);
for(int i=1;i<=n;i++) b[i]=abs(b[i]);
init();
while(q--){
int op;
cin>>op;
if(op==1){
int x,l,r;
ll v;
cin>>x>>v;
l=fi[x],r=lst[x];
add(l,r,v);
}
else{
int x,l,r;
cin>>x;
l=fi[x],r=lst[x];
cout<<search(l,r)<<"\n";
}
}

return true&&false;
}