【bzoj2402】陶陶的难题II 【树链剖分+线段树+斜率优化+二分】
题目大意:
解法:
二分答案m,转化为判断性问题。
这样我们就把答案分成了独立的两部分,两边都取最大即可。以第一部分举例。
若k比j优,则
这是一个非常标准的斜率优化的形式。
这一题是树链上的询问,所以我们可以树链剖分套线段树,线段树上递减维护斜率。我们就递减维护。先把每个线段树节点的单调递减的斜率处理出来,查询时二分到第一个小于m的斜率就是解。然后在线段树上取个max即可。最后判断一下两部分的和是否大于等于0。
吐槽:WA的我要死啊!最后把输出四位改成输出五位就A了?你逗我呢?
代码:
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;
const int N=30005;
const double eps=1e-5;
int n,m,u,v,cnt,head[N],to[N*2],nxt[N*2];
int idx,siz[N],fa[N],dep[N],son[N],top[N],dfn[N],pos[N];
double x[N],y[N],p[N],q[N];
void adde(int u,int v){
to[++cnt]=v;
nxt[cnt]=head[u];
head[u]=cnt;
}
void dfs(int u){
siz[u]=1;
int v;
for(int i=head[u];i;i=nxt[i]){
v=to[i];
if(v!=fa[u]){
fa[v]=u;
dep[v]=dep[u]+1;
dfs(v);
siz[u]+=siz[v];
if(!son[u]||siz[son[u]]<=siz[v]){
son[u]=v;
}
}
}
}
void dfs(int u,int tp){
dfn[u]=++idx;
pos[idx]=u;
top[u]=tp;
if(son[u]){
dfs(son[u],tp);
}
int v;
for(int i=head[u];i;i=nxt[i]){
v=to[i];
if(v!=fa[u]&&v!=son[u]){
dfs(v,v);
}
}
}
struct data{
double x,y;
};
double getx(data a,data b){
return b.y-a.y;
}
double gety(data a,data b){
return b.x-a.x;
}
struct SegmentTree{
vector<data> v1[N*4],v2[N*4];
void build(int o,int l,int r,double *x,double *y){
if(l==r){
v1[o].push_back((data){x[pos[l]],y[pos[l]]});
v2[o].push_back((data){x[pos[l]],y[pos[l]]});
return;
}
int mid=(l+r)/2;
build(o*2,l,mid,x,y);
build(o*2+1,mid+1,r,x,y);
for(int i=0,j=0,k=0;i<r-l+1;i++){
if(k>=r-mid){
v2[o].push_back(v2[o*2][j++]);
}else if(j>=mid-l+1){
v2[o].push_back(v2[o*2+1][k++]);
}else if(v2[o*2][j].x<v2[o*2+1][k].x){
v2[o].push_back(v2[o*2][j++]);
}else{
v2[o].push_back(v2[o*2+1][k++]);
}
}
for(int i=0;i<r-l+1;i++){
while(v1[o].size()>1&&getx(v1[o][v1[o].size()-1],v2[o][i])*gety(v1[o][v1[o].size()-2],v1[o][v1[o].size()-1])>=getx(v1[o][v1[o].size()-2],v1[o][v1[o].size()-1])*gety(v1[o][v1[o].size()-1],v2[o][i])){
v1[o].pop_back();
}
v1[o].push_back(v2[o][i]);
}
}
double get(int o,double m){
int l=0,r=v1[o].size()-1;
while(l<r){
int mid=(l+r)/2;
if(getx(v1[o][mid],v1[o][mid+1])<m*gety(v1[o][mid],v1[o][mid+1])){
r=mid;
}else{
l=mid+1;
}
}
return v1[o][r].y-v1[o][r].x*m;
}
double query(int o,int l,int r,int L,int R,double m){
if(L==l&&R==r){
return get(o,m);
}
int mid=(l+r)/2;
if(R<=mid){
return query(o*2,l,mid,L,R,m);
}else if(L>mid){
return query(o*2+1,mid+1,r,L,R,m);
}else{
return max(query(o*2,l,mid,L,mid,m),query(o*2+1,mid+1,r,mid+1,R,m));
}
}
}s1,s2;
bool check(int u,int v,double m){
double a=-1e18,b=-1e18;
while(top[u]!=top[v]){
if(dep[top[u]]<dep[top[v]]){
swap(u,v);
}
a=max(a,s1.query(1,1,n,dfn[top[u]],dfn[u],m));
b=max(b,s2.query(1,1,n,dfn[top[u]],dfn[u],m));
u=fa[top[u]];
}
if(dep[u]>dep[v]){
swap(u,v);
}
a=max(a,s1.query(1,1,n,dfn[u],dfn[v],m));
b=max(b,s2.query(1,1,n,dfn[u],dfn[v],m));
return a+b>=0;
}
int main(){
scanf("%d",&n);
for(int i=1;i<=n;i++){
scanf("%lf",&x[i]);
}
for(int i=1;i<=n;i++){
scanf("%lf",&y[i]);
}
for(int i=1;i<=n;i++){
scanf("%lf",&p[i]);
}
for(int i=1;i<=n;i++){
scanf("%lf",&q[i]);
}
for(int i=1;i<n;i++){
scanf("%d%d",&u,&v);
adde(u,v);
adde(v,u);
}
dfs(1);
dfs(1,1);
s1.build(1,1,n,x,y);
s2.build(1,1,n,p,q);
scanf("%d",&m);
for(int i=1;i<=m;i++){
scanf("%d%d",&u,&v);
double l=0,r=1e8;
while(r-l>eps){
double mid=(l+r)/2;
if(check(u,v,mid)){
l=mid;
}else{
r=mid;
}
}
printf("%.5lf\n",l);
}
return 0;
}