P5114 八月脸
简单题,但是我太菜了。树上路径问题考虑树分治,这里是点分治。
观察答案的形式为 \(ka+b\) 的最大值,这个很简单就可以用凸包维护。
在点分治过程中,记每个点 \(x\) 到根上路径的点权和为 \(a_x,b_x\)。对于一个根的每一颗子树,维护只有黑点和白点的凸包,答案(凸包)的形式是一个黑色凸包和一个白色凸包的闵可夫斯基和。
为了复杂度,这个东西可以合并果子,就是每次掏出最小的两颗子树做和,然后把凸包里的点扔进一个点集里,最后分别合并两个子树的黑白凸包。
最后对得到的点集求一个凸包,查询直接在上面二分或者扫一遍就行。
分析复杂度,闵可夫斯基和是线性的,瓶颈在于点分治过程中产生的点数,即刚才那个点分治上合并果子的复杂度。
不难发现(真的不难嘛)这个过程等价于在点分树上做启发式合并,故只有一只 \(\log\)。算上求凸包的排序需要两只 \(\log\)(不过双关键字排序可以线性做,理论更优是只带一只)。
以下的代码实现是 \(O(n\log n\log(n\log n))\) 的。
diss 下造数据的,合并果子写成大根堆甚至能得 \(88\) 分,调了若干个小时,结果是复杂度错了。
#include<iostream>
#include<queue>
#include<vector>
#include<algorithm>
using pii = std::pair<int, int>;
using ll = long long; using ld = double;
const int N = 1e5 + 10; std::vector<int> g[N];
long long ans[N]; std::pair<int, int> q[N];
int n, m, nn, mx, rt, rc, ct[2], c[N], sz[N], vs[N];
struct nd{
ll x, y; ll f(int k){return k * x + y;}
nd operator+(const nd&a)const{return {x+a.x,y+a.y};}
nd operator-(const nd&a)const{return {x-a.x,y-a.y};}
bool operator>=(const nd&a)const{return x*a.y<=y*a.x;}
}rs[N<<5], t[2][N], w[N];
struct lrc{
int n; std::vector<nd> a;
lrc(){n=0;} void clear(){n=0,a.clear();}
nd operator[](const int&x)const{return a[x];}
void ins(const nd&x){
while(n>1&&(x-a[n-1])>=(a[n-1]-a[n-2]))
--n, a.pop_back(); ++n, a.push_back(x);
}
}a[N<<1][2], tm;
lrc mg(const lrc&a, const lrc&b){
tm.clear();
for(int i = 0, j = 0; i < a.n || j < b.n;)
if(i==a.n||(j<b.n&&b[j].x<a[i].x)) tm.ins(b[j++]);
else if(j==b.n||a[i].x<b[j].x) tm.ins(a[i++]);
else tm.ins(a[i].y>b[j].y?a[i]:b[j]),i++,j++;
return tm;
}
lrc minkow(const lrc &a,const lrc &b){
tm.clear(); if(!a.n || !b.n) return tm;
tm.ins(a[0] + b[0]);
for(int i = 0, j = 0; i < a.n - 1 || j < b.n - 1;)
if(i==a.n-1) tm.ins(a[i] + b[++j]);
else if(j==b.n-1) tm.ins(a[++i] + b[j]);
else tm.ins((a[i+1]-a[i])>=(b[j+1]-b[j])?a[++i]+b[j]:a[i]+b[++j]);
return tm;
}
void fd(int x, int fa){
sz[x] = 1; int s = 0;
for(auto v:g[x]) if(v != fa && !vs[v])
fd(v, x), sz[x] += sz[v], s = std::max(s, sz[v]);
s = std::max(s, nn - sz[x]); if(s < mx) mx = s, rt = x;
}
void dfs(int x, int fa, nd p){
t[c[x]][++ct[c[x]]] = p;
for(auto v:g[x]) if(v != fa && !vs[v]) dfs(v, x, p + w[v]);
}
inline bool cmp(const nd&a,const nd&b){return a.x==b.x?a.y<b.y:a.x<b.x;}
void bd(int u){
a[u][0].clear(); a[u][1].clear();
std::sort(t[0] + 1, t[0] + ct[0] + 1, cmp);
std::sort(t[1] + 1, t[1] + ct[1] + 1, cmp);
for(int i = 1; i <= ct[0]; i++) a[u][0].ins(t[0][i]);
for(int i = 1; i <= ct[1]; i++) a[u][1].ins(t[1][i]);
}
void calc(int x, int y, int u){
tm = minkow(a[x][0], a[y][1]);
for(int i=0;i<tm.n;i++) rs[++rc]=tm[i]+w[u];
tm = minkow(a[x][1], a[y][0]);
for(int i=0;i<tm.n;i++) rs[++rc]=tm[i]+w[u];
}
void div(int x){
vs[x] = 1; std::priority_queue<pii, std::vector<pii>, std::greater<pii>> q;
for(auto v:g[x]) if(!vs[v]){
ct[0] = ct[1] = 0, dfs(v, x, w[v]);
bd(v), q.push({a[v][0].n+a[v][1].n, v});
} int o = n; while(q.size() > 1){
std::pair<int, int> xx, yy;
xx = q.top(); q.pop(); yy = q.top(); q.pop();
calc(xx.second, yy.second, x); ++o;
a[o][0] = mg(a[xx.second][0], a[yy.second][0]);
a[o][1] = mg(a[xx.second][1], a[yy.second][1]);
q.push({a[o][0].n + a[o][1].n, o});
} if(!q.size()) return; int t = q.top().second; q.pop();
if(c[x]) for(int i=0;i<a[t][0].n;i++) rs[++rc]=a[t][0][i]+w[x];
if(!c[x])for(int i=0;i<a[t][1].n;i++) rs[++rc]=a[t][1][i]+w[x];
for(auto v:g[x]) if(!vs[v]) nn=mx=sz[v], fd(v,x), div(rt);
}
int main(){
std::ios::sync_with_stdio(false);
std::cin.tie(0),std::cout.tie(0);
std::cin >> n >> m;
for(int i = 1; i <= n; i++) std::cin >> w[i].x;
for(int i = 1; i <= n; i++) std::cin >> w[i].y;
for(int i = 1; i <= n; i++) std::cin >> c[i];
for(int i = 1, x, y; i < n; i++)
std::cin >> x >> y,
g[x].push_back(y), g[y].push_back(x);
nn = mx = n, fd(1, 0), div(rt);
std::sort(rs + 1, rs + rc + 1, cmp); tm.clear();
for(int i = 1; i <= rc; i++) tm.ins(rs[i]);
for(int i = 1, x; i <= m; i++)
std::cin >> x, q[i] = {x, i};
if(!tm.n){for(int i=1;i<=m;i++) puts("0"); exit(0);}
std::sort(q + 1, q + m + 1);
for(int i = 1, j = 0; i <= m; i++){
auto [k, p] = q[i];
while(j+1<tm.n && tm[j+1].f(k)>=tm[j].f(k))
j++; ans[p] = tm[j].f(k);
} for(int i = 1; i <= m; i++) std::cout << ans[i] << '\n';
}