zoukankan      html  css  js  c++  java
  • POJ 3728 The merchant (树形DP+LCA)

    题目:https://vjudge.net/contest/323605#problem/E

    题意:一棵n个点的树,然后有m个查询,每次查询找(u->v)路径上的两个数,a[i],a[j],(i<j)a[j]-a[i]的最大值,j必须是u->v路径上出现的比i晚

    思路:首先我们路径肯定是确定只有一条的,然后我们怎么找出那条路径呢,我们可以求LCA,求出u->LCA(u,v)   LCA(u,v)->v  ,这样我们就能把路径给确定出来

    然后我们先简化问题,如果是一个序列,我们要找两个数的最大差值,我们可以维护一个单调栈,然后每次求最大差值,复杂度为O(n),我们可以先用LCA把路径求出来,然后直接O(n)遍历出来即可,但是查询数量有 <=50000,会超时,这个时候我们只能想能不能预处理一些有用的东西,然后O(1)查询出来,因为LCA复杂度为O(logn)*(O(m)查询数)复杂度正好,我们可以优化上述算法,首先我们肯定和最大值最小值有关,我们求出每个点到LCA的最小值,和LCA到当前点的最大值,然后如何练习起来呢,其实我们可以把路径合并,首先两个点之间间隔一条边,肯定就是max(value[v]-value[u],0),然后合并的时候有一个转移方程,max(u->LCA(u,v)的利润,LCA(u,v)->v的利润,max(LCA(u,v)->v)-min(u,LCA(u,v))   ), 为什么呢下面给出三个例子

     例子一,这个就是用maxvalue-minvalue

     例子二,这个就是u->LCA(u,v)情况

     例子三,这个就是LCA(u,v)->v的情况

    然后差不多就可以解出来了,因为本人对LCA还不会太操作,然后就没写代码了,发现自己思路是对的,就直接贴别人代码了>_<

    来源:https://blog.csdn.net/xingyeyongheng/article/details/20402603

    /*分析:先求出点u,v的最近公共祖先f,然后求u->f->v的利润最大值maxval
    对于这个maxval可能有三种情况:
    1:maxval是u->f的maxval
    2:maxval是f->v的maxval
    3:maxval是u->f的最小w[i]减去f->v的最大w[i]
    分析到这很明显需要设置4个变量来求maxval:
    up[u]表示u->f的最大maxval
    down[u]表示f->u的最大maxval
    maxw[u]表示u-f的最大w[i]
    minw[u]表示u-f的最小w[i]
    所以maxval=max(max(up[u],down[v]),maxw[v]-minw[u]);
    现在问题就是如何快速的求出这四个变量,在这里我们可以对u,v的LCA(u,v)进行分类解决
    对于LCA(u,v)是f的询问全部求出,然后再求LCA(u,v)是f的父亲的询问
    这样当我们求LCA(u,v)是f的父亲的询问的时候就可以借用已经求出的LCA(u,v)是f的询问
     的结果,这样就不用反复去求u->f的那四个变量值,u->father[f]也能快速求出
     这个变化主要在寻找father[v]这个过程中进行,具体看代码 
    */
    #include <iostream>
    #include <cstdio>
    #include <cstdlib>
    #include <cstring>
    #include <string>
    #include <queue>
    #include <algorithm>
    #include <map>
    #include <cmath>
    #include <iomanip>
    #define INF 99999999
    typedef long long LL;
    using namespace std;
     
    const int MAX=50000+10;
    int n,m,size;
    int uu[MAX],vv[MAX],ww[MAX],sum[MAX];
    int up[MAX],down[MAX],maxw[MAX],minw[MAX],father[MAX];
    int head[MAX],head2[MAX],head3[MAX];
    bool mark[MAX];
     
    struct Edge{
        int v,id,next;
        Edge(){}
        Edge(int V,int ID,int NEXT):v(V),id(ID),next(NEXT){}
    }edge[MAX*2],edge2[MAX*2],edge3[MAX*2];
     
    void Init(int num){
        for(int i=0;i<=num;++i)head[i]=head2[i]=head3[i]=-1,mark[i]=false;
        size=0; 
    }
     
    void InsertEdge(int u,int v,int id){
        edge[size]=Edge(v,id,head[u]);
        head[u]=size++;
    }
     
    void InsertEdge2(int u,int v,int id){
        edge2[size]=Edge(v,id,head2[u]);
        head2[u]=size++;
    }
     
    void InsertEdge3(int u,int v,int id){
        edge3[size]=Edge(v,id,head3[u]);
        head3[u]=size++;
    }
     
    int findset(int v){
        if(v == father[v])return father[v];
        int fa=father[v];
        father[v]=findset(father[v]);
        up[v]=max(max(up[v],up[fa]),maxw[fa]-minw[v]);
        down[v]=max(max(down[v],down[fa]),maxw[v]-minw[fa]);
        maxw[v]=max(maxw[v],maxw[fa]);
        minw[v]=min(minw[v],minw[fa]);
        return father[v];
    }
     
    void LCA(int u){
        mark[u]=true;
        father[u]=u;
        for(int i=head2[u];i != -1;i=edge2[i].next){//对LCA(u,v)进行分类 
            int v=edge2[i].v,id=edge2[i].id;
            if(!mark[v])continue;
            int f=findset(v);
            InsertEdge3(f,v,id);
        }
        for(int i=head[u];i != -1;i=edge[i].next){
            int v=edge[i].v;
            if(mark[v])continue;
            LCA(v);
            father[v]=u;
        }
        for(int i=head3[u];i != -1;i=edge3[i].next){
            int id=edge3[i].id;
            findset(uu[id]);
            findset(vv[id]);
            sum[id]=max(max(up[uu[id]],down[vv[id]]),maxw[vv[id]]-minw[uu[id]]);
        }
    }
     
    int main(){
        int u,v;
        while(~scanf("%d",&n)){
            Init(n);
            for(int i=1;i<=n;++i){
                scanf("%d",ww+i);
                up[i]=down[i]=0;
                maxw[i]=minw[i]=ww[i];
            }
            for(int i=1;i<n;++i){
                scanf("%d%d",&u,&v);
                InsertEdge(u,v,i);
                InsertEdge(v,u,i); 
            }
            size=0;
            scanf("%d",&m);
            for(int i=0;i<m;++i){
                scanf("%d%d",&uu[i],&vv[i]);
                InsertEdge2(uu[i],vv[i],i);
                InsertEdge2(vv[i],uu[i],i); 
            }
            size=0;
            LCA(1);
            for(int i=0;i<m;++i)printf("%d
    ",sum[i]);
        }
        return 0;
    }
  • 相关阅读:
    【原创】C#零基础学习笔记010-数据流技术
    【原创】C#零基础学习笔记009-异常处理
    【原创】C#零基础学习笔记008-C#集合处理
    【原创】C#零基础学习笔记007-面向对象的特性
    【原创】C#零基础学习笔记006-面向对象编程基础
    【原创】C#零基础学习笔记005-字符串和日期
    【原创】C#零基础学习笔记004-数组
    session
    最简朴的 session 来实现 登录验证
    cookies session 代码例子
  • 原文地址:https://www.cnblogs.com/Lis-/p/11461735.html
Copyright © 2011-2022 走看看