BZOJ2402: 陶陶的难题II(点分治)

本文介绍了一种结合点分树和分数规划算法解决特定问题的方法。通过建立点分树并利用分数规划思想进行查询,文章详细阐述了如何在多个凸包上进行高效的二分查询,最终实现对每一对顶点间最优解的快速计算。

传送门

题解:
建出点分树,之后对于每个询问二分答案 mid m i d ,用分数规划的思想,相当于求 max{yxmid} max { y − x ∗ m i d } 。最优点在凸包上,之后对于每个分治中心都处理出所有子节点到他的路径上的凸包(用dfs序做),然后在 logn log ⁡ n 个凸包上二分查询切点,时间复杂度 O(nlog3n) O ( n log 3 ⁡ n )

#include <bits/stdc++.h>
#define rdd(x) scanf("%d",&x)
#define rdf(x) scanf("%lf",&x)
typedef double DB;
using namespace std;
const int N=2e5+50;
const DB eps=1e-6;
inline int sgn(DB x) {return (x>eps)-(x<-eps);}
struct P {
    DB x,y;
    P(DB x=0,DB y=0):x(x),y(y){}
    friend inline P operator -(const P &a,const P &b) {return P(a.x-b.x,a.y-b.y);}
    friend inline DB operator *(const P &a,const P &b) {return a.x*b.y-a.y*b.x;}
    inline DB dis() {return x*x+y*y;}
}st;
inline bool cmp(const P &a,const P &b) {
    if(sgn(a.x-b.x)) return sgn(a.x-b.x)<0;
    return sgn(a.y-b.y)<0;
}
struct data {
    int x,y,id;
    data(int x,int y,int id):x(x),y(y),id(id){}
};
int n,m,G,d[N],fa[N],dfn[N],vis[N],sze[N],mx,total,ind;
vector <int> edge[N];
vector <data> qry[N];
DB xc[N],yc[N],p[N],q[N],ans[N];
inline int getlca(int x,int y) {
    while(x!=y) {
        (d[x]<d[y])?y=fa[y]:x=fa[x];
    } return x;
}
inline void calcG(int x,int f) {
    sze[x]=1; int mxv=0;
    for(int e=edge[x].size()-1;e>=0;e--) {
        int v=edge[x][e]; if(v==f || vis[v]) continue;
        calcG(v,x); 
        sze[x]+=sze[v];
        mxv=max(mxv,sze[v]);
    }
    mxv=max(mxv,total-sze[x]);
    if(mx>mxv) mx=mxv,G=x;
}
inline void dfs(int x,int f) {
    sze[x]=1;
    for(int e=edge[x].size()-1;e>=0;e--) {
        int v=edge[x][e]; if(v==f || vis[v]) continue;
        dfs(v,x); sze[x]+=sze[v];
    }
}
inline void solve(int x,int f) {
    fa[x]=f; d[x]=d[f]+1; vis[x]=1;
    for(int e=edge[x].size()-1;e>=0;e--) {
        int v=edge[x][e]; if(vis[v]) continue;
        dfs(v,x); mx=(total=sze[v]);
        calcG(v,x); solve(G,x);
    }
}
vector <P> conv[2][N];
vector <P> point[2][N];
vector <DB> slope[2][N];
int vc[N],vt;
inline void clear(int k,int l,int r) {
    conv[0][k].clear(); point[0][k].clear(); slope[0][k].clear();
    conv[1][k].clear(); point[1][k].clear(); slope[1][k].clear();
    if(l==r) return;
    int mid=(l+r)>>1;
    clear(k<<1,l,mid); clear(k<<1|1,mid+1,r);
}
inline void build_conv(vector <P> &pt,vector <P> &cv,vector <DB> &sp) {
    if(!pt.size()) return;
    sort(pt.begin(),pt.end(),cmp);
    for(int i=0;i<pt.size();++i) {
        if(i!=pt.size()-1 && !sgn(pt[i+1].x-pt[i].x)) continue;
        while(cv.size()>=2 && sgn((cv[cv.size()-1]-cv[cv.size()-2])*(pt[i]-cv[cv.size()-2]))>=0) cv.pop_back();
        cv.push_back(pt[i]);
    }
    sp.push_back(-1e9);
    for(int i=1;i<cv.size();++i) 
        sp.push_back(-(cv[i].y-cv[i-1].y)/(cv[i].x-cv[i-1].x));
}
inline void build(int k,int l,int r) {
    for(int o=0;o<2;++o) 
        build_conv(point[o][k],conv[o][k],slope[o][k]);
    if(l==r) return;
    int mid=(l+r)>>1;
    build(k<<1,l,mid); 
    build(k<<1|1,mid+1,r);
}
inline void inc(int k,int l,int r,int L,int R,int v) {
    if(L<=l&&r<=R) {
        point[0][k].push_back(P(xc[v],yc[v]));
        point[1][k].push_back(P(p[v],q[v]));
        return;
    }
    int mid=(l+r)>>1;
    if(R<=mid) inc(k<<1,l,mid,L,R,v);
    else if(L>mid) inc(k<<1|1,mid+1,r,L,R,v);
    else inc(k<<1,l,mid,L,R,v),inc(k<<1|1,mid+1,r,L,R,v);
}
inline void dfs2(int x,int f,int top) {
    sze[x]=1; dfn[x]=++ind;
    for(int e=edge[x].size()-1;e>=0;e--) {
        int v=edge[x][e]; if(v==f || d[v]<=d[top]) continue;
        dfs2(v,x,top); sze[x]+=sze[v];
    }
}
inline void dfs3(int x,int f,int top) {
    inc(1,1,ind,dfn[x],dfn[x]+sze[x]-1,x);
    for(int e=edge[x].size()-1;e>=0;e--) {
        int v=edge[x][e]; if(v==f || d[v]<=d[top]) continue;
        dfs3(v,x,top);
    }
}
inline DB query(const vector <P> &cv,const vector <DB> &sp,DB val) {
    if(!sp.size()) return -1e9;
    int p=(--lower_bound(sp.begin(),sp.end(),-val))-sp.begin();
    return -cv[p].x*val+cv[p].y;
}
inline DB query(int k,int l,int r,int pos,DB val,int o) {
    DB mx=query(conv[o][k],slope[o][k],val);
    if(l==r) return mx;
    int mid=(l+r)>>1;
    if(pos<=mid) return max(mx,query(k<<1,l,mid,pos,val,o));
    else return max(mx,query(k<<1|1,mid+1,r,pos,val,o));
}
inline bool check(int x,int y,DB val) {
    DB mx1=max(query(1,1,ind,dfn[x],val,0),query(1,1,ind,dfn[y],val,0));
    DB mx2=max(query(1,1,ind,dfn[x],val,1),query(1,1,ind,dfn[y],val,1));
    return sgn(mx1+mx2)>=0;
}
inline DB calc(int x,int y) {
    DB l=0,r=1e5;
    while(sgn(r-l)>0) {
        DB mid=(l+r)/2.0;
        if(check(x,y,mid)) l=mid;
        else r=mid;
    } return l;
}
inline void calc(int x) {
    ind=0;
    dfs2(x,0,fa[x]);
    clear(1,1,ind);
    dfs3(x,0,fa[x]);
    build(1,1,ind);
    for(int i=0;i<qry[x].size();++i) 
        ans[qry[x][i].id]=calc(qry[x][i].x,qry[x][i].y);
}
int main() {
    rdd(n);
    for(int i=1;i<=n;i++) rdf(xc[i]); 
    for(int i=1;i<=n;i++) rdf(yc[i]);
    for(int i=1;i<=n;i++) rdf(p[i]);
    for(int i=1;i<=n;i++) rdf(q[i]);  
    for(int i=1;i<n;i++) {
        int x,y; rdd(x); rdd(y);
        edge[x].push_back(y);
        edge[y].push_back(x);
    } 
    mx=(total=n); 
    calcG(1,0); 
    solve(G,0);
    rdd(m);
    for(int i=1;i<=m;i++) {
        int x,y; rdd(x); rdd(y);
        int lca=getlca(x,y);
        qry[lca].push_back(data(x,y,i));
    }
    for(int i=1;i<=n;i++)
        if(qry[i].size()) calc(i);
    for(int i=1;i<=m;i++)
        printf("%.5f\n",ans[i]);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值