• hiho 1995 树上的最短边


    hiho 1995 树上的最短边

    https://hihocoder.com/problemset/problem/1995

    题目

    给定一棵包含N个节点的带权树,节点编号1~N。小Hi每次会给定树上两个节点的编号u和v,请你计算从u到v的路径上,哪条边的权值最小。

    请你输出最小的权值。

    思路

    显然是个lca,倍增lca的时候加个维护边权就可以了。但是我拒绝
    脑子抽筋想写个点分治,点分治做起来也很简单,先把询问离线,重心合并的时候就看某次询问在不在重心划分出的不同子树上,是的话遍历时候更新一下就好了。时间复杂度O(nlogn)

    代码

    #include<bits/stdc++.h>
    using namespace std;
    #define X first
    #define Y second
    #define PB push_back
    #define MP make_pair
    #define MEM(a,b) memset(a,b,sizeof(a))
    typedef long long ll;
    const ll mod = 1e9+7;
    const int maxn =1e6+10;
    ll n,k;
    int N,K;
    int root,Max;
    struct node{
        int v,next,w;
    }edge[maxn*2];
    
    int head[maxn],tot,num1,num2;
    int si[maxn],maxv[maxn],vis[maxn];//vis标记重心
    
    vector<pair<int,int>> E[maxn];
    int cnt[maxn];
    int ans[maxn];
    const int inf =1e9+7;
    
    
    void init(){
        tot=0;MEM(head,-1);MEM(vis,0);
    }
    void add_edge(int u,int v,int w){
        edge[tot].v=v;edge[tot].w=w;edge[tot].next=head[u];head[u]=tot++;
    }
    //处理子树的大小(固定)
    void dfssi(int u,int f){
        si[u]=1;
        maxv[u]=0;
        for(int i=head[u];i!=-1;i=edge[i].next){
            int v=edge[i].v;
            if(v==f||vis[v])continue;
            dfssi(v,u);
            si[u]+=si[v];
            if(si[v]>maxv[u])maxv[u]=si[v];
        }
    }
    //找重心(固定)
    void dfsroot(int r,int u,int f){
        if(si[r]-si[u]>maxv[u])//si[r]-si[u]是u上面部分的树的尺寸,跟u的最大孩子比,找到最大孩子的最小差值节点
            maxv[u]=si[r]-si[u];
        if(maxv[u]<Max)Max=maxv[u],root=u;
        for(int i=head[u];i!=-1;i=edge[i].next){
            int v=edge[i].v;
            if(v==f||vis[v])continue;
            dfsroot(r,v,u);
        }
    }
    
    //(自定义)
    
    void dfs_count(int u,int f,int col){
        cnt[u]=col;
        for(int i=head[u];i!=-1;i=edge[i].next){
            int v=edge[i].v;
            if(v!=f&&!vis[v])//不跨过其他重心
                dfs_count(v,u,col);
        }
    }
    
    void dfs_ans(int u,int d,int f){
        for(auto c:E[u]){
            int x=u,y=c.first,id=c.second;
            if(cnt[y]!=0&&cnt[y]!=cnt[x])
                ans[id]=min(d,ans[id]);
        }
        for(int i=head[u];i!=-1;i=edge[i].next){
            int v=edge[i].v;
            if(v!=f&&!vis[v])//不跨过其他重心
                dfs_ans(v,min(edge[i].w,d),u);
        }
    }
    
    //分边解决一个重心的答案(自定义)
    int calc(int u){
        int ret=0;
        num1=0;
        cnt[u]=inf;
        int col=1;
        for(int i=head[u];i!=-1;i=edge[i].next){
            int v=edge[i].v,w=edge[i].w;
            if(!vis[v]){
                dfs_count(v,v,col++);
            }
        }
        for(int i=head[u];i!=-1;i=edge[i].next){
            int v=edge[i].v,w=edge[i].w;
            if(!vis[v]){
                dfs_ans(v,w,v);
            }
        }
        for(int i=head[u];i!=-1;i=edge[i].next){
            int v=edge[i].v,w=edge[i].w;
            if(!vis[v]){
                dfs_count(v,v,0);
            }
        }
        cnt[u]=0;
        return ret;
    }
    
    void dfs(int u){
        Max=N;
        dfssi(u,0);//重心预处理
        dfsroot(u,u,0);//找重心
        vis[root]=1;
        calc(root);//合并贡献(固定)
        for(int i=head[root];i!=-1;i=edge[i].next){
            int v=edge[i].v;
            if(!vis[v])dfs(v);
        }
    }
    
    int main(){
        scanf("%d%d",&N,&K);
        {
            int u,v,w,rt;
            init();
            for(int i=1;i<=N;i++){
                scanf("%d%d",&u,&v);
                if(u==0){
                    rt=i;
                 continue;
                }
                add_edge(i,u,v);add_edge(u,i,v);
            }
            for(int i=0;i<K;i++){
                scanf("%d%d",&u,&v);
                E[u].push_back({v,i});
                E[v].push_back({u,i});
            }
            for(int i=0;i<=K;i++) ans[i]=inf;
            dfs(rt);
            for(int i=0;i<K;i++)
                printf("%d
    ",ans[i]);
        }
        return 0;
    }
    
    
  • 相关阅读:
    Android开发之SQLite的使用方法
    【转】如何分析解决Android ANR
    error log
    33层高楼为什么27楼和28楼最贵 次顶层房价高原因揭秘
    Could not allocate CursorWindow size due to error -12 错误解决方法
    过来人讲述买房血泪史:什么样的房子不能碰
    cocos2d-x删除vs2010项目模板
    Lua学习笔记5:类及继承的实现
    Linux vsftpd服务配置具体解释
    Android_Dialog_设置Dialog窗体的大小
  • 原文地址:https://www.cnblogs.com/zhangxianlong/p/11567760.html
Copyright © 2020-2023  润新知