传送门
解析:
先就本题聊两句:本题出题人shadowice1984退役了,对,就是sjzez那位大毒瘤shadowice1984,每次打他id我都要抱怨这名字怎么这么长的的那位shadowice1984,就是那个常年活跃在洛谷各大毒瘤题题解区和讨论区的那个shadowice1984。TA退役了。
也正是看了他的退役记后我才决定要把这道我本来想着永远咕掉的毒瘤题写出来,并卡到洛谷rank1(也做到了),算是对这位毒瘤的一种纪念吧。
正题:
首先我们撇开那个可(gui)爱(chu)的动图不谈。(为什么这么多可爱的妹子重叠在一起这么鬼畜啊啊啊啊)
显然这是一个斜率优化的式子,答案一定在上凸壳上,那么我们想的是求出所有路径的权值后求一个凸包 ,想办法把所有在凸包上的路径搞出来。
首先这种路径最优化问题显然需要上树分治,这道题点分治会发现不好写(似乎复杂度都是错的?我觉得是,没有细想),我们直接用边分治搞出两个部分的上凸壳,然后用Minkowski Sum合并就行了。
注意在边分治的转化过程中,不注意处理就会丢掉信息。
原题题解中的做法是手动将LCA处的信息补上去。但是其实我们可以转成边权(见下方代码),然后在最终的答案中实际上每个点是算了两倍的,除以2就行了。
代码:
#include<bits/stdc++.h>
#define ll long long
#define re register
#define gc get_char
#define cs const
namespace IO{
inline char get_char(){
static cs int Rlen=1<<22|1;
static char buf[Rlen],*p1,*p2;
return (p1==p2)&&(p2=(p1=buf)+fread(buf,1,Rlen,stdin),p1==p2)?EOF:*p1++;
}
template<typename T>
inline T get(){
re char c;
re bool f=0;
while(!isdigit(c=gc()))f=c=='-';re T num=c^48;
while(isdigit(c=gc()))num=(num+(num<<2)<<1)+(c^48);
return f?-num:num;
}
inline int getint(){return get<int>();}
}
using namespace IO;
using std::cerr;
using std::cout;
struct Point{
ll x,y;
Point(){}
Point(cs ll &_x,cs ll &_y):x(_x),y(_y){}
friend Point operator+(cs Point &a,cs Point &b){return Point(a.x+b.x,a.y+b.y);}
friend Point operator-(cs Point &a,cs Point &b){return Point(a.x-b.x,a.y-b.y);}
friend ll operator*(cs Point &a,cs Point &b){return a.x*b.y-a.y*b.x;}
};
template<int SIZE>
struct Polygon{
Point p[SIZE],d[SIZE];
int siz;
inline void push_back(cs Point &a){p[siz++]=a;}
inline void build_convex(){
std::sort(p,p+siz,[&](cs Point &a,cs Point &b){return a.x<b.x;});
int re i=0;
for(int re j=0;++j<siz;){
while(d[i]=p[j]-p[i],i&&d[i]*d[i-1]<=0)--i;
if(!i&&!d[0].x&&d[0].y>=0)--i;
p[++i]=p[j];
}
siz=i;
}
template<int S1,int S2>
inline void merge(Polygon<S1> &F,Polygon<S2> &G){
if(!F.siz||!G.siz)return ;
F.build_convex();
G.build_convex();
Point *i=p+siz++;
*i=F.p[0]+G.p[0];
Point *q=std::merge(F.d,F.d+F.siz,G.d,G.d+G.siz,i+1,[&](cs Point &a,cs Point &b){return a*b<0;});
while(++i<q)*i=(*i)+*(i-1);
siz=q-p;
}
inline void print(){
cerr<<"-----------------------------\n";
cerr<<"siz : "<<siz<<"\n";
for(int i=0;i<siz;++i)cerr<<p[i].x<<" "<<p[i].y<<"\n";
cerr<<"-----------------------------\n";
}
inline ll query(ll k){
int i=std::upper_bound(d,d+siz,-k,[&](ll k,cs Point &a){return a.y<k*a.x;})-d;
return p[i].x*k+p[i].y>>1;
}
};
cs int N=1e5+5;
int n,m;
std::vector<int> E[N];
Polygon<N> H[4],*cur;
Polygon<N*20> ans;
int last[N<<1],nxt[N<<2],to[N<<2],ecnt=1;
Point w[N<<1],val[N];
inline void addedge(int u,int v,cs Point &val=Point(0,0)){
nxt[++ecnt]=last[u],last[u]=ecnt,to[ecnt]=v;
nxt[++ecnt]=last[v],last[v]=ecnt,to[ecnt]=u;
w[ecnt>>1]=val;
}
inline void rebuild(int u,int fa){
int son=E[u].size(),nd=u;
for(int re v:E[u])if(v^fa){
if(--son>2){
addedge(nd,++n);
addedge(nd=n,v,val[u]+val[v]);
}
else {
addedge(nd,v,val[u]+val[v]);
nd=u;
}
rebuild(v,u);
}
}
bool c[N],ban[N<<1];
int siz[N<<1],son[N<<1],pre[N<<1];
int get_siz(int u,int fa){
siz[u]=u<=n;int mx=0;
for(int re e=last[u],v=to[e];e;v=to[e=nxt[e]])if(v!=fa&&!ban[e>>1]){
int t=get_siz(v,u);
if(mx<t)mx=t,son[u]=v;
siz[u]+=t,pre[v]=e;
}
return siz[u];
}
void dfs(int u,int fa,cs Point &p){
if(u<=n)cur[c[u]].push_back(p+val[u]);
for(int re e=last[u],v=to[e];e;v=to[e=nxt[e]])
if(v!=fa&&!ban[e>>1])dfs(v,u,p+w[e>>1]);
}
void divide(int u){
int S=get_siz(u,0),v,e;
while(siz[v=son[u]]>(S>>1))u=v;
if(siz[u]>=S-siz[v])e=pre[v];
else v=to[e=pre[u]^1];
ban[e>>1]=true;
H[0].siz=H[1].siz=H[2].siz=H[3].siz=0;
cur=H;dfs(u,0,Point(0,0));
cur+=2;dfs(v,0,w[e>>1]);
bool uok=H[0].siz&&H[1].siz,vok=H[2].siz&&H[3].siz;
ans.merge(H[0],H[3]);
ans.merge(H[1],H[2]);
if(uok)divide(u);
if(vok)divide(v);
}
signed main(){
// freopen("august.in","r",stdin);//freopen("august.out","w",stdout);
n=getint(),m=getint();
for(int re i=1;i<=n;++i)val[i].x=getint();
for(int re i=1;i<=n;++i)val[i].y=getint();
for(int re i=1;i<=n;++i)c[i]=getint();
for(int re i=1,u,v;i<n;++i,u=getint(),v=getint(),E[u].push_back(v),E[v].push_back(u));
int n=::n;rebuild(1,0);
::n=n;divide(1);
ans.build_convex();
while(m--){
ll k=getint();
cout<<ans.query(k)<<"\n";
}
return 0;
}