【Kruskal,LCA】NOIP2013提高组Day1 货车运输

前言

先把这个题目变好看一点。。。
K r u s k a l L C A N O I P 2013 提高组 D a y 1 货车运输
这道题比 R M Q 那道题难多了,竟然都是蓝色的。。。

链接

https://www.luogu.org/problemnew/show/P1967

大意

给定一个 n 个点, m 条无向边的图,每条路都有一定的限重,现在有 q 个货物,求不超过限重的情况下可以带最重的货物

思路

以1为根建造一棵最大生成树( K r u s k a l )
然后再用倍增跑 L C A

代码

#include<cstdio>
#include<algorithm>
#define N 20001//n要开两倍
#define M 50001
#define r(i,n) for(int i=1;i<=n;i++)
using namespace std;
struct node{int x,y,z;}v[M];//到时候要用边排序
int n,m,q,fa[N],f[N],p[N],d[N][20],deep[N];//fa是用于Kruskal的并查集数组,f是在倍增中用的并查集数组,我在前面LCA的博客中也有提到
bool cmp(node x,node y){ return x.z>y.z;}//按照载重从大到小排序
int find(int x){return fa[x]==x?x:fa[x]=find(fa[x]);}//并查集
int Lca(int x,int y)//求LCA
{
       int t,i=0;
       if(deep[x]<deep[y]) {x^=y;y=x^y;x^=y;}//交换x和y
       while(i>=0)//这里都是LCA
       {
           while(deep[d[x][i]]>=deep[y]) x=d[x][i++];
           i--;
       }
       i=0;
       while(f[x]!=f[y])//同理
       {
           while(d[x][i]!=d[y][i]) x=d[x][i],y=d[y][i],i++;
           i--;
       }
       return f[x];//返回
}
int main()
{
    scanf("%d%d",&n,&m); int bx,by,tot=0,w=n,k,x,y;
    r(i,m) scanf("%d%d%d",&v[i].x,&v[i].y,&v[i].z);
    sort(v+1,v+m+1,cmp);//排序
    r(i,n) fa[i]=i;//初始化
    r(i,m)//以下为Kruskal
    {
        bx=find(v[i].x);
        by=find(v[i].y);
        if(bx!=by)
        {
            fa[by]=fa[bx]=f[bx]=f[by]=++w;
            p[w]=v[i].z;
            fa[w]=w;
            if(++tot==n-1) break;
        }
    }
    n=w;deep[n]=0;
    for(int i=n-1;i;i--)
    {
        deep[i]=deep[f[i]]+1;
        d[i][0]=f[i];k=0;
        while(d[d[i][k]][k])d[i][k+1]=d[d[i][k]][k],k++;
    }//这上面的相当于代替了dfs+倍增,详细可以去看https://blog.csdn.net/xuxiayang/article/details/80369618#t5
    scanf("%d",&q);
    while(q--)
    {
        scanf("%d%d",&x,&y);
        printf("%d\n",(find(x)==find(y))?p[Lca(x,y)]:-1);//若联通,输出它们到LCA的长度,否则输出-1
    }
    return 0;
}

猜你喜欢

转载自blog.csdn.net/xuxiayang/article/details/80436984