【bzoj2402】陶陶的难题II 【树链剖分+线段树+斜率优化+二分】

题目大意:
这里写图片描述
解法:
二分答案m,转化为判断性问题。
(y[i]+q[j])/(x[i]+p[j])>=m
y[i]+q[j]>=x[i]m+p[j]m
(y[i]x[i]m)+(q[j]p[j]m)>=0
这样我们就把答案分成了独立的两部分,两边都取最大即可。以第一部分举例。
若k比j优,则
y[k]x[k]m>=y[j]x[j]m
y[k]y[k]>=x[k]mx[j]m
y[k]y[j]>=(x[k]x[j])m
(y[k]y[j])/(x[k]x[j])>=m
这是一个非常标准的斜率优化的形式。
这一题是树链上的询问,所以我们可以树链剖分套线段树,线段树上递减维护斜率。我们就递减维护(y[k]y[j])/(x[k]x[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
",l);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/2016gdgzoi471/p/9476885.html