• poj3728 倍增法lca 好题!


    lca的好题!网上用st表和离线解的比较多,用树上倍增也是可以做的

    不知道错在哪里,等刷完了这个专题再回来看

    题解链接https://blog.csdn.net/Sd_Invol/article/details/9572423

    /*                    
    给一颗点权树,求出一个点对(x,y)之间的max{A,B,C}
    A:x到lca路径上的最大差值
    B:lca到y路径上的最大差值
    C:x到y路径上的最大差值
    需要维护的值,x结点到的祖先,x结点到祖先路径上的最大值,最小值,x结点到路径上的最大收益,最小收益(可以是负数)
    对于每个询问x->y:A:x到祖先的最大收益
                     B:y到祖先的最小收益的负值
                     C:x到lca的最大值减去lca到y的最小值
    */
    #include<iostream>    
    #include<cstring>    
    #include<cstdio>    
    #include<cmath>    
    #include<algorithm>
    using namespace std;
    #define maxn 50005
    struct Edge{
        int x,next;
    }e[maxn<<1];
    int head[maxn],tot,n,q;
    inline void addedge(int u,int v){
        e[tot].x=v;
        e[tot].next=head[u];
        head[u]=tot++;
    }            
    int d[maxn],f[maxn][16],a[maxn];//深度,祖先,点权
    int fm[maxn][16],fn[maxn][16],sm[maxn][16],sn[maxn][16];//最大值,最小值,最大收益,最小收益
    void init(){
        tot=0;    
        memset(head,-1,sizeof head);
    }            
    void dfs(int x,int fa,int dep){
        d[x]=dep,f[x][0]=fa;
        for(int i=head[x];i!=-1;i=e[i].next)
            if(fa!=e[i].x) dfs(e[i].x,x,dep+1);
    }            
    int query(int x,int y){
        int i,xx=0,yy=0,X=a[x],Y=a[y];//x侧最大收益,y侧最小收益,x侧最小值,y侧最大值
        i=15;    
        while(d[x]!=d[y]){//拉倒同一高度
            if(abs(d[x]-d[y]) >= 1<<i)
                if(d[y]<d[x]) 
                    xx=max(max(xx,sm[x][i]),fm[x][i]-X),X=min(X,fn[x][i]),x=f[x][i];
                else 
                    yy=min(min(yy,sn[y][i]),fn[y][i]-Y),Y=max(Y,fm[y][i]),y=f[y][i];
            --i;
        }        
        if(x==y) return max(max(xx,-yy),Y-X);
        i=15;    
        while(i>=0){
            if(f[x][i] && f[y][i] && f[x][i]!=f[y][i]){
                xx=max(max(xx,sm[x][i]),fm[x][i]-X),X=min(X,fn[x][i]),x=f[x][i];
                yy=min(min(yy,sn[y][i]),fn[y][i]-Y),Y=max(Y,fm[y][i]),y=f[y][i];
            }    
            --i;
        }        
        i=0;//这里还要跳一次
        xx=max(max(xx,sm[x][i]),fm[x][i]-X),X=min(X,fn[x][i]),x=f[x][i];
        yy=min(min(yy,sn[y][i]),fn[y][i]-Y),Y=max(Y,fm[y][i]),y=f[y][i];
        return max(max(xx,-yy),Y-X);
    }            
    void work(){
        int x,y;
        init();    
        scanf("%d",&n);
        for(int i=1;i<=n;i++) scanf("%d",&a[i]);
        for(int i=1;i<n;i++){
            scanf("%d%d",&x,&y);
            addedge(x,y);addedge(y,x);
        }        
        dfs(1,0,0);
        fm[1][0]=fn[1][0]=a[1];
        sm[1][0]=-1<<30;sn[1][0]=1<<30;
        for(int i=2;i<=n;i++){//先打初始状态
            fm[i][0]=max(a[i],a[f[i][0]]);//和父亲比较
            fn[i][0]=min(a[i],a[f[i][0]]);
            sm[i][0]=max(0,a[f[i][0]]-a[i]);//要么是0,要么赚了钱
            sn[i][0]=min(0,a[f[i][0]]-a[i]);//要么是0,要么是亏了钱
        }        
        for(int j=1;(1<<j)<n;j++)//再打剩下状态
            for(int i=1;i<=n;i++){
                int tmp=f[i][j-1];//中间态
                f[i][j]=f[tmp][j-1];
                fm[i][j]=max(fm[i][j-1],fm[tmp][j-1]);
                fn[i][j]=min(fm[i][j-1],fm[tmp][j-1]);
                sm[i][j]=max(max(sm[i][j-1],sm[tmp][j-1]),fm[tmp][j-1]-fn[i][j-1]);//最大收益要么是两段间的最大收益,要么是祖先段的最大收益减去子孙段的最小收益
                sn[i][j]=min(min(sn[i][j-1],sn[tmp][j-1]),fn[tmp][j-1]-fm[i][j-1]);//最小收益相反
            }    
        scanf("%d",&q);
        while(q--){
            scanf("%d%d",&x,&y);
            printf("%d
    ",query(x,y));
        }
    }
    int main(){
        work();
        return 0;
    }
  • 相关阅读:
    包含中文的js文件在从cdn搞到本地时造成的问题;
    查询杀死进程
    二维码显示在网页上
    二维码生成器和解析器-java
    N个任务,分配给M个人,那么每个人得到的任务数量----总结经验
    js中文转换成拼音
    Grails连接mysql数据库
    Grails框架+Intellij IDEA工具,写了一个对字符串进行转码,包括纯js转成Base64格式
    java冒泡排序
    对文字简单的加密解密
  • 原文地址:https://www.cnblogs.com/zsben991126/p/10028622.html
Copyright © 2020-2023  润新知