[BZOJ2402] 陶陶的难题II 题解
这题一脸 01 分数规划的长相,考虑对答案进行二分。
设最大答案为 \(cmax\),则当 \((-x_ic+y_i)+(-p_jc+q_j)\ge 0\) 时,\(c\le cmax\)。不等式左半侧前后两个部分相互独立且解法相近,可以分别处理。我们只考虑 \(-x_ic+y_i\) 的最大值,这显然是一个可以凸包求解的东西。对树进行树链剖分,问题转化成区间问题,经典线段树维护凸包即可。
时间复杂度 \(O(n\log^4n)\)。
#include<bits/stdc++.h>
#define fs(x) fixed<<setprecision(x)
using namespace std;
const int N=30005,M=1e5+5;
int n,m,tmp[N],dfn[N],ps[N],sk[N],tps;
struct seg{
double xc[N],yc[N];vector<int>st[M];
double jd(int x,int y){
return (yc[x]-yc[y])/(xc[y]-xc[x]);
}int cmp(int x,int y,int z){
return jd(x,y)>jd(y,z);
}void build(int x,int l,int r){
for(int i=l;i<=r;i++) tmp[i-l+1]=ps[i];
sort(tmp+1,tmp+r-l+2,[&](int x,int y){return xc[x]<xc[y];}),tps=0;
for(int i=1;i<=r-l+1;sk[++tps]=tmp[i++])
while(tps>1&&cmp(sk[tps-1],sk[tps],tmp[i])) tps--;
for(int i=1;i<=tps;i++) st[x].push_back(sk[i]);
if(l==r) return;int mid=(l+r)/2;
build(x*2,l,mid),build(x*2+1,mid+1,r);
}double maxn(int x,int l,int r,int L,int R,double nw){
if(L<=l&&r<=R){
int lc=1,rc=st[x].size()-1,ans=st[x][0];
while(lc<=rc){
int mid=(lc+rc)/2,e=st[x][mid-1],f=st[x][mid];
if(xc[e]*nw+yc[e]>xc[f]*nw+yc[f]) rc=mid-1;
else lc=mid+1,ans=st[x][mid];
}return xc[ans]*nw+yc[ans];
}int mid=(l+r)/2;double re=-1e18;
if(R>mid) re=maxn(x*2+1,mid+1,r,L,R,nw);
if(L<=mid) re=max(re,maxn(x*2,l,mid,L,R,nw));
return re;
}
}tr1,tr2;vector<int>g[N];
int fa[N],sz[N],sn[N],tp[N],dep[N],id;
void dfs1(int x,int ft){
dep[x]=dep[ft]+1;
for(auto y:g[x]) if(y!=ft){
dfs1(y,x),sz[x]+=sz[y];
if(sz[sn[x]]<sz[y]) sn[x]=y;
}sz[x]++,fa[x]=ft;
}void dfs2(int x,int top){
tp[x]=top,dfn[x]=++id,ps[id]=x;
if(!sn[x]) return;dfs2(sn[x],top);
for(auto y:g[x]) if(!dfn[y]) dfs2(y,y);
}int check(int x,int y,double nw){
double amx=-1e18,bmx=-1e18;
while(tp[x]!=tp[y]){
if(dep[tp[x]]<dep[tp[y]]) swap(x,y);
amx=max(amx,tr1.maxn(1,1,n,dfn[tp[x]],dfn[x],nw));
bmx=max(bmx,tr2.maxn(1,1,n,dfn[tp[x]],dfn[x],nw));
x=fa[tp[x]];
}if(dep[x]<dep[y]) swap(x,y);
amx=max(amx,tr1.maxn(1,1,n,dfn[y],dfn[x],nw));
bmx=max(bmx,tr2.maxn(1,1,n,dfn[y],dfn[x],nw));
return amx+bmx>=0;
}int main(){
ios::sync_with_stdio(0);
cin.tie(0),cout.tie(0);
cin>>n;double c;
for(int i=1;i<=n;i++) cin>>c,tr1.xc[i]=-c;
for(int i=1;i<=n;i++) cin>>tr1.yc[i];
for(int i=1;i<=n;i++) cin>>c,tr2.xc[i]=-c;
for(int i=1;i<=n;i++) cin>>tr2.yc[i];
for(int i=1,x,y;i<n;i++)
cin>>x>>y,g[x].push_back(y),g[y].push_back(x);
cin>>m,dfs1(1,0),dfs2(1,1);
tr1.build(1,1,n),tr2.build(1,1,n);
while(m--){
int x,y;cin>>x>>y;
double l=0,r=2e9,ans=0;
while(r-l>=1e-7){
double mid=(l+r)/2;
if(!check(x,y,mid)) r=mid-1e-7;
else l=mid+1e-7,ans=mid;
}cout<<fs(4)<<ans<<"\n";
}return 0;
}