【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\n");
            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\n",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\n",ans);
    }
    return 0;
}

完毕。

posted @ 2017-09-28 06:23  Konoset  阅读(138)  评论(0编辑  收藏  举报