zoukankan      html  css  js  c++  java
  • 【51nod】1766 树上的最远点对

    【题意】给定n个点的树,m次求[a,b]和[c,d]中各选出一个点的最大距离。abcd是标号区间,n,m<=10^5

    【算法】LCA+树的直径理论+线段树

    【题解】

    树的直径性质:距离树上任意点最远的点一定是直径的一端。此结论在点集中依然试用。

    那么根据性质,容易得到答案路径的两端一定是[a,b]直径的一端和[c,d]直径的一端的连线

    (考虑任意一个点集AB的点,在点集A中距离最远的是a或b,在点集B中距离最远的是c或d,故直径的端点只能是abcd)

    从而,两个区间的直径可以快速合并成一个区间的直径,即直径的可并性

    因为直径可并和区间标号连续,所以可以用线段树维护一段区间的直径并查询。

    连线用LCA解决,倍增常数较大会TLE,使用树链剖分或RMQ-LCA皆可。

    复杂度O(n log2n)。

    【注意】

    RMQ查询时注意判断l>r时swap。

    线段树合并时要从左右子树内部取答案,而询问不能从内部取答案。

    #include<cstdio>
    #include<cstring>
    #include<cctype>
    #include<algorithm>
    using namespace std;
    const int maxn=100010;
    int read(){
        char c;int s=0,t=1;
        while(!isdigit(c=getchar()))if(c=='-')t=-1;
        do{s=s*10+c-'0';}while(isdigit(c=getchar()));
        return s*t;
    }
    int first[maxn],a[maxn*3],b[maxn],logs[maxn*3],d[maxn*3][50],p[maxn*3][50],deep[maxn],dis[maxn],tot,cnt;
    int L[maxn*4],R[maxn*4],n,m;
    struct edge{int v,w,from;}e[maxn*2];
    struct cyc{int num,A,B;}t[maxn*4];
    
    int min(int a,int b){return a<b?a:b;}
    int max(int a,int b){return a<b?b:a;}
    void insert(int u,int v,int w){cnt++;e[cnt].v=v;e[cnt].w=w;e[cnt].from=first[u];first[u]=cnt;}
    void dfs(int x,int fa){
        a[++tot]=x;b[x]=tot;
        for(int i=first[x];i;i=e[i].from)if(e[i].v!=fa){
            deep[e[i].v]=deep[x]+1;
            dis[e[i].v]=dis[x]+e[i].w;
            dfs(e[i].v,x);
            a[++tot]=x;
        }
    }
    void RMQ_init(){
        logs[0]=-1;for(int i=1;i<=tot;i++)logs[i]=logs[i>>1]+1;
        for(int i=1;i<=tot;i++)d[i][0]=deep[a[i]],p[i][0]=i;
        for(int j=1;(1<<j)<=tot;j++){
            for(int i=1;i+(1<<j)-1<=tot;i++)if(d[i][j-1]<d[i+(1<<(j-1))][j-1]){
                d[i][j]=d[i][j-1];p[i][j]=p[i][j-1];
            }else{d[i][j]=d[i+(1<<(j-1))][j-1];p[i][j]=p[i+(1<<(j-1))][j-1];}
        }
    }
    int lca(int l,int r){
        l=b[l],r=b[r];
        if(l>r)swap(l,r);//
        int k=logs[r-l+1];
        k=d[l][k]<d[r-(1<<k)+1][k]?p[l][k]:p[r-(1<<k)+1][k];
        return dis[a[l]]+dis[a[r]]-2*dis[a[k]];
    }
    cyc tr(bool ok,cyc L,cyc R){
        cyc x=(cyc){-1,0,0};
        if(!ok){
            if(L.A==0)return R;
            if(R.A==0)return L;
            x=L;
            if(R.num>x.num)x=R;
        }
        int num=lca(L.A,R.A);
        if(num>x.num)x=(cyc){num,L.A,R.A};
        num=lca(L.A,R.B);
        if(num>x.num)x=(cyc){num,L.A,R.B};
        num=lca(L.B,R.A);
        if(num>x.num)x=(cyc){num,L.B,R.A};
        num=lca(L.B,R.B);
        if(num>x.num)x=(cyc){num,L.B,R.B};
        return x;
    }
    void build(int k,int l,int r){
        L[k]=l;R[k]=r;
        if(l==r)t[k]=(cyc){0,l,r};
        else{
            int mid=(l+r)>>1;
            build(k<<1,l,mid);build(k<<1|1,mid+1,r);
            t[k]=tr(0,t[k<<1],t[k<<1|1]);
        }
    }
    cyc ask(int k,int l,int r){
        if(l<=L[k]&&R[k]<=r)return t[k];
        else{
            int mid=(L[k]+R[k])>>1;
            cyc x=(cyc){0,0,0};
            if(l<=mid)x=ask(k<<1,l,r);
            if(r>mid)x=tr(0,x,ask(k<<1|1,l,r));
            return x;
        }
    }
    int main(){
        n=read();
        for(int i=1;i<n;i++){
            int u=read(),v=read(),w=read();
            insert(u,v,w);insert(v,u,w);
        }
        tot=0;dfs(1,0);RMQ_init();build(1,1,n);
        m=read();
        for(int i=1;i<=m;i++){
            int x=read(),y=read(),z=read(),w=read();
            cyc q=tr(1,ask(1,x,y),ask(1,z,w));
            printf("%d
    ",q.num);
        }
        return 0;
    }    
    View Code
  • 相关阅读:
    测试数据生成利器
    9.22“月饼杯”递归算法欢乐赛测试报告总结
    lemon评测软件配置使用方法
    1200:分解因数
    大犇博客
    C++ |递归原理与构造技巧
    2018.9.8信息奥赛集训评测报告总结
    1195 判断整除
    计算机图形学初步
    AcWing
  • 原文地址:https://www.cnblogs.com/onioncyc/p/7651578.html
Copyright © 2011-2022 走看看