poj3728之离线LCA+dp思想/RMQ+LCA(非常好的题目)

本文探讨了一种结合树形动态规划与最近公共祖先(LCA)算法的问题解决策略,旨在优化路径上的商品交易利润计算。通过分析树状结构中的节点属性,如最大利润、最小成本等,实现路径上最佳买卖点的快速定位。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题意很简单

给一个树(n < 5w) 每个点有个权值,代表商品价格

若干个询问(5w)

 

对每个询问,问的是从u点走到v点(简单路径),商人在这个路径中的某点买入商品,然后在某点再卖出商品,   最大可能是多少

注意一条路径上只能买卖一次,先买才能卖

 

  • *分析:先求出点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]这个过程中进行,具体看代码
  •  
    /*分析:先求出点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\n",sum[i]);
        }
        return 0;
    }
    View Code

     

RMQ做法

#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,top;
int uu[MAX],vv[MAX],ww[MAX],anc[MAX];
int up[MAX][20],down[MAX][20],maxw[MAX][20],minw[MAX][20],deep[MAX];
int head[MAX],head2[MAX],bin[MAX],stack[MAX],mp[MAX][20],father[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];
 
void Init(int num){
    for(int i=0;i<=num;++i)head[i]=head2[i]=-1,mark[i]=false;
    size=top=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 dfs(int u,int father,int k){
    deep[u]=k;
    for(int i=head[u];i != -1;i=edge[i].next){
        int v=edge[i].v;
        if(v == father)continue;
        dfs(v,u,k+1);
    }
}
void RMQ(int u,int father){
    stack[++top]=u;
    int fa=stack[top-1];
    up[u][0]=down[u][0]=0;
    maxw[u][0]=minw[u][0]=ww[u];
    for(int i=1;bin[i]<=top;++i){//2^i<=top
        fa=stack[top-bin[i-1]];
        up[u][i]=max(max(up[u][i-1],up[fa][i-1]),maxw[fa][i-1]-minw[u][i-1]);
        down[u][i]=max(max(down[u][i-1],down[fa][i-1]),maxw[u][i-1]-minw[fa][i-1]);
        maxw[u][i]=max(maxw[u][i-1],maxw[fa][i-1]);
        minw[u][i]=min(minw[u][i-1],minw[fa][i-1]);
        mp[u][i]=stack[top-bin[i]];
    }
    for(int i=head[u];i != -1;i=edge[i].next){
        int v=edge[i].v;
        if(v == father)continue;
        RMQ(v,u);
    }
    --top;
}
int findset(int v){
    if(v != father[v])father[v]=findset(father[v]);
    return father[v];
}
void LCA(int u){
    mark[u]=true;
    father[u]=u;
    for(int i=head2[u];i != -1;i=edge2[i].next){
        int v=edge2[i].v,id=edge2[i].id;
        if(!mark[v])continue;
        anc[id]=findset(v);
    }
    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;
    }
}
int search(int x){
    int i=0;
    while(bin[i+1]<=x)++i;
    return i;
}
int Minw(int u,int anc){
    int i=search(deep[u]-deep[anc]+1);
    if(bin[i] == deep[u]-deep[anc]+1)return minw[u][i];
    return min(minw[u][i],Minw(mp[u][i],anc));
}
int Maxw(int u,int anc){
    int i=search(deep[u]-deep[anc]+1);
    if(bin[i] == deep[u]-deep[anc]+1)return maxw[u][i];
    return max(maxw[u][i],Maxw(mp[u][i],anc));
}
int Down(int u,int anc){
    int i=search(deep[u]-deep[anc]+1);
    if(bin[i] == deep[u]-deep[anc]+1)return down[u][i];
    int downfa=Down(mp[u][i],anc);
    downfa=max(downfa,down[u][i]);
    int minwfa=Minw(mp[u][i],anc);
    return max(downfa,maxw[u][i]-minwfa); 
}
int UP(int u,int anc){
    int i=search(deep[u]-deep[anc]+1);
    if(bin[i] == deep[u]-deep[anc]+1)return up[u][i];
    int upfa=UP(mp[u][i],anc);
    upfa=max(upfa,up[u][i]);
    int maxwfa=Maxw(mp[u][i],anc);
    return max(upfa,maxwfa-minw[u][i]);
}
int main(){
    bin[0]=1;
    for(int i=1;bin[i-1]<MAX;++i)bin[i]=bin[i-1]*2;
    int u,v;
    while(~scanf("%d",&n)){
        Init(n);
        for(int i=1;i<=n;++i)scanf("%d",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);
        }
        dfs(1,-1,1);
        RMQ(1,-1);
        LCA(1);    
        for(int i=0;i<m;++i){
            int upmax=UP(uu[i],anc[i]),downmax=Down(vv[i],anc[i]);    
            int Minww=Minw(uu[i],anc[i]),Maxww=Maxw(vv[i],anc[i]);
            printf("%d\n",max(max(upmax,downmax),Maxww-Minww));
        }
    }
    return 0;
}
/*
7
300
11
11
21
10
31
222
1 2
2 3
3 4
4 5
2 6
1 7
1
5 6
*/
View Code

 

转载于:https://www.cnblogs.com/shuaihui520/p/9703223.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值