• 【Luogu】P1967货车运输(最大生成森林+倍增LCA)


      题目链接

      倍增LCA是个什么蛇皮原理啊,循环完了还得再往上跳一次才能到最近公共祖先

      合着我昨天WA两次就是因为这个

      建最大生成森林,因为图不一定是联通的,所以不一定是一棵树。这个地方用克鲁斯卡尔就好了

      然后给这个森林跑一遍DFS,顺便倍增

      然后对于每个询问跑LCA,倍增的时候已经顺便求出了最小边权,所以往上跳的同时更新答案。

      代码如下

    #include<cstdio>
    #include<cctype>
    #include<cstdlib>
    #include<cmath>
    #include<algorithm>
    #include<cstring>
    inline long long read(){
        long long num=0,f=1;
        char ch=getchar();
        while(!isdigit(ch)){
            if(ch=='-')    f=-1;
            ch=getchar();
        }
        while(isdigit(ch)){
            num=num*10+ch-'0';
            ch=getchar();
        }
        return num*f;
    }
    
    struct EDGE{
        int from,to,dis;
        bool operator <(const EDGE &a)const{
            return dis>a.dis;
        }
    }que[1000010];
    
    int father[100010];
    struct Edge{
        int next,to,dis;
    }edge[1000010];
    int head[500010],num;
    inline void add(int from,int to,int dis){
        edge[++num]=(Edge){head[from],to,dis};
        head[from]=num;
    }
    
    int find(int x){
        if(father[x]!=x)    father[x]=find(father[x]);
        return father[x];
    }
    
    inline void unionn(int x,int y){
        x=find(x);y=find(y);
        father[y]=x;
    }
    
    int deep[100010];
    int d[10010][33];
    int s[10010][33];
    
    void dfs(int x,int fa){
        deep[x]=deep[fa]+1;
        for(int i=head[x];i;i=edge[i].next){
            int to=edge[i].to;
            if(to==fa)    continue;
            d[to][0]=x;
            s[to][0]=edge[i].dis;
            dfs(to,x);
        }
    }
    int cnt;
    int main(){
        int n=read(),m=read();
        for(int i=1;i<=m;++i){
            int from=read(),to=read(),dis=read();
            que[i]=(EDGE){from,to,dis};
        }
        std::sort(que+1,que+m+1);
        for(int i=1;i<=n;++i)    father[i]=i;
        for(int i=1;i<=m;++i){
            int from=que[i].from,to=que[i].to,dis=que[i].dis;
            if(find(from)==find(to))    continue;
            unionn(from,to);
            add(from,to,dis);
            add(to,from,dis);
            if(++cnt==n-1)    break;
        }
        for(int i=1;i<=n;++i)
            if(!deep[i])    dfs(i,i);
        for(int j=1;(1<<j)<=n;++j)
            for(int i=1;i<=n;++i){
                d[i][j]=d[d[i][j-1]][j-1];
                s[i][j]=std::min(s[d[i][j-1]][j-1],s[i][j-1]);
            }
        int Q=read();
        for(int i=1;i<=Q;++i){
            int from=read(),to=read();
            if(find(from)!=find(to)){
                printf("-1
    ");
                continue;
            }
            if(deep[from]<deep[to])    std::swap(from,to);
            int x=deep[from]-deep[to],ans=0x7fffffff;
            for(int j=0;(1<<j)<=x;++j)
                if((1<<j)&x){
                    ans=std::min(ans,s[from][j]);
                    from=d[from][j];
                }
            if(from==to){
                printf("%d
    ",ans);
                continue;
            }
            for(int j=log2(n);j>=0;--j)
                if(d[from][j]!=d[to][j]){
                    ans=std::min(ans,std::min(s[from][j],s[to][j]));
                    from=d[from][j];
                    to=d[to][j];
                }
            ans=std::min(ans,std::min(s[from][0],s[to][0]));
            printf("%d
    ",ans);
        }
        return 0;
    }

    完毕。

  • 相关阅读:
    C#中 File,Directory,FileInfo,DirectoryInfo区别与应用
    C#中设置开机自动运行和关机
    C# 线程手册 第三章 使用线程 小心死锁
    C# WinForm判断Win7下是否是管理员身份运行
    C#应用MemoryStream提高File读取速度
    CodeforcesDouble Profiles
    SRM533 D1 L1
    SRM532 D1 L2
    SRM533 D2 L3
    次小生成树 | 割点 | 割边
  • 原文地址:https://www.cnblogs.com/cellular-automaton/p/7604777.html
Copyright © 2020-2023  润新知