zoukankan      html  css  js  c++  java
  • 【洛谷P1967】货车运输

    题目

    题目链接:https://www.luogu.com.cn/problem/P1967
    A 国有 (n) 座城市,编号从 $1 $ 到 $ n$,城市之间有 (m) 条双向道路。每一条道路对车辆都有重量限制,简称限重。

    现在有 (q) 辆货车在运输货物, 司机们想知道每辆车在不超过车辆限重的情况下,最多能运多重的货物。

    思路

    Kruskal 重构树模板题。
    Kruskal 重构树一般解决的问题是“给出一张无向图,询问 (x,y) 两点之间所有路径中最短边长度最长的边的长度”。
    我们将边权从大到小排序,在 Kruskal 求最大生成树时,如果 (x',y') 两点并查集的根节点 (x,y) 不同,那么就建立一个新点 (p),从 (p) 分别向 (x)(y) 连边,然后将 (x,y) 两者的父亲都指向 (p)。点 (p) 的点权即为这条边的长度。
    这样对于一组询问 (x,y),我们直接在 Kruskal 重构树上求出其 LCA,答案显然就是该点的权值。
    时间复杂度 (O(Qlog n+m))

    代码

    #include <bits/stdc++.h>
    using namespace std;
    
    const int N=20010,M=50010,LG=18;
    int n,m,Q,tot,val[N],head[N],father[N],dep[N],f[N][LG+1];
    
    struct edge1
    {
    	int u,v,dis;
    }e1[M];
    
    struct edge2
    {
    	int next,to;
    }e2[M];
    
    void add(int from,int to)
    {
    	e2[++tot]=(edge2){head[from],to};
    	head[from]=tot;
    }
    
    bool cmp(edge1 x,edge1 y)
    {
    	return x.dis>y.dis;
    }
    
    int find(int x)
    {
    	return x==father[x]?x:father[x]=find(father[x]);
    }
    
    void kruskal()
    {
    	sort(e1+1,e1+1+m,cmp);
    	for (int i=1;i<n*2;i++) father[i]=i;
    	for (int i=1;i<=m;i++)
    	{
    		int x=find(e1[i].u),y=find(e1[i].v);
    		if (x!=y)
    		{
    			n++; val[n]=e1[i].dis;
    			add(n,x); add(n,y);
    			father[x]=father[y]=n;
    		}
    	}
    }
    
    void dfs(int x,int fa)
    {
    	dep[x]=dep[fa]+1; f[x][0]=fa;
    	for (int i=1;i<=LG;i++)	
    		f[x][i]=f[f[x][i-1]][i-1];
    	for (int i=head[x];~i;i=e2[i].next)
    		dfs(e2[i].to,x);
    }
    
    int lca(int x,int y)
    {
    	if (dep[x]<dep[y]) swap(x,y);
    	for (int i=LG;i>=0;i--)
    		if (dep[f[x][i]]>=dep[y]) x=f[x][i];
    	if (x==y) return x;
    	for (int i=LG;i>=0;i--)
    		if (f[x][i]!=f[y][i]) x=f[x][i],y=f[y][i];
    	return f[x][0];
    }
    
    int main()
    {
    	memset(head,-1,sizeof(head));
    	scanf("%d%d",&n,&m);
    	for (int i=1;i<=m;i++)
    		scanf("%d%d%d",&e1[i].u,&e1[i].v,&e1[i].dis);
    	kruskal();
    	for (int i=1;i<=n;i++)
    		if (find(i)==i) dfs(i,0);
    	scanf("%d",&Q);
    	while (Q--)
    	{
    		int x,y;
    		scanf("%d%d",&x,&y);
    		if (find(x)!=find(y)) printf("-1
    ");
    			else printf("%d
    ",val[lca(x,y)]);
    	}
    	return 0;
    }
    
  • 相关阅读:
    C#面向对象的基本原则
    EXTJS学习笔记:类似于Window的登录窗体
    c# 类似于QQ表情弹出框功能的二种实现方法
    EXTJS学习笔记:grid之分组实现groupingview
    RibbonBar屏蔽右键
    webservice安全性浅谈
    js生成二维码以及插入图片
    c++之const解惑
    最短路径连接表形式
    深入理解c++之动态内存和指针
  • 原文地址:https://www.cnblogs.com/stoorz/p/14082447.html
Copyright © 2011-2022 走看看