P1967 货车运输 题解

Posted bifanwen

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了P1967 货车运输 题解相关的知识,希望对你有一定的参考价值。

CSDN同步

原题链接

简要题意:

给定一个无向图,若干组询问问 (x ightarrow y) 所有路径上最小权值的最大值。

算法一

对于 (60\%) 的数据,(1 le n < 10^3,1 le m < 5 imes 10^4,1 le q< 10^3)

如果您有网络流板子的话,可以认为,本题是 给定网络图,若干组流量询问,直接 ( ext{ISAP}) 乱跑就可以得到 (O(qnm)) 的优秀时间,轻松得到 (60pts) 及以上。(网络流算法时间跑不满)

说句闲话:这题网络流怎么优化

时间复杂度:(O(qnm)). 期望得分:(60pts). 实际得分 100pts

算法二

对于 (100\%) 的数据,(1 le n < 10^4 , 1 le m < 5 imes 10^4 , 1 le q< 3 imes 10^4 , 0 le z le 10^5).

首先,我们考虑的是,如果,(x ightarrow u (w_1))(u ightarrow v (w_2)) ,与 (x ightarrow v (w_3)) 同时存在的话,如果 (min(w_1,w_2) leq w_3),可以直接把 (w_3) 扔掉;走的时候肯定是走 (x ightarrow w ightarrow v) 了。

那么,我们得到了网络流的贪心算法 显然,我们需要对每一组 (x ightarrow y) 都把这样的 废边 去掉。

嗯?所以我们要把所有 (x ightarrow y) 的最大路径取出来,对吧?

那么,最大生成树 就映入我们眼帘:显然这是 最大生成树重构图

求最大生成树并只留下这个树上的边(重构),那么 (x ightarrow y) 肯定是最大路径(不然肯定换边了)。

那么如何应对询问?显然,我们在 树上求两点路径( ext{LCA}) 操作即可。

注意细节。

时间复杂度:(O(n log n + q log n + m)).

实际得分:(100pts).

#pragma GCC optimize(2)
#include<bits/stdc++.h>
using namespace std;

const int N=1e5+1;

inline int read(){char ch=getchar(); int f=1;while(ch<‘0‘ || ch>‘9‘) {if(ch==‘-‘) f=-f; ch=getchar();}
	int x=0;while(ch>=‘0‘ && ch<=‘9‘) x=(x<<3)+(x<<1)+ch-‘0‘,ch=getchar();return x*f;}

struct EE1 {int x,y,dis;} E1[N];
struct E2 {int to,nxt,w;} E2[N];
int head[N],n,m,f[N],deep[N],cnt,q;
int fa[N][21],w[N][21]; bool vis[N];

inline void add(int u,int v,int w) {
	if(u==v) return;
	E2[++cnt].nxt=head[u];
	E2[cnt].to=v; E2[cnt].w=w;
	head[u]=cnt; return;
}

inline int find(int x) {return f[x]==x?x:f[x]=find(f[x]);}
inline bool cmp(EE1 x,EE1 y) {return x.dis>y.dis;}

inline void kruskal() {
	sort(E1+1,E1+1+m,cmp);
	for(int i=1;i<=n;i++) f[i]=i;
	for(int i=1;i<=m;i++) {
		int u=E1[i].x,v=E1[i].y,w=E1[i].dis;
		if(find(u)-find(v)) {
			f[find(u)]=find(v);
			add(u,v,w); add(v,u,w);
		} 
	}
} //最大生成树模板

inline void dfs(int u) {
	vis[u]=1;
	for(int i=head[u];i;i=E2[i].nxt) {
		int v=E2[i].to,t=E2[i].w;
		if(vis[v]) continue;
		deep[v]=deep[u]+1;
		fa[v][0]=u; w[v][0]=t;
		dfs(v);
	} return;
} //预处理父亲

inline int LCA(int x,int y) {
	if(find(x)-find(y)) return -1;
	int ans=INT_MAX;
	if(deep[x]>deep[y]) swap(x,y);
	for(int i=20;i>=0;i--)
        if(deep[fa[y][i]]>=deep[x]) ans=min(ans,w[y][i]),y=fa[y][i]; //调整深度
    if(x==y) return ans;
    for(int i=20;i>=0;i--)
        if(fa[x][i]-fa[y][i]) {
            ans=min(ans,min(w[x][i],w[y][i]));
            x=fa[x][i]; y=fa[y][i];
        } //一起跳
    return min(ans,min(w[x][0],w[y][0])); //最后答案
}

int main() {
	n=read(),m=read();
	for(int i=1;i<=m;i++) {
		int u=read(),v=read(),w=read();
//		add(u,v,w); add(v,u,w);
		E1[i].x=u; E1[i].y=v; E1[i].dis=w;
	} kruskal();
	for(int i=1;i<=n;i++)
		if(!vis[i]) {
			deep[i]=1; dfs(i);
			fa[i][0]=i; w[i][0]=INT_MAX;
		} q=read();
	for(int i=1;i<=20;i++)
    for(int j=1;j<=n;j++) {
        fa[j][i]=fa[fa[j][i-1]][i-1]; //fa[j][i] 为 j 向上走 2^i 个节点到达的节点
        w[j][i]=min(w[j][i-1],w[fa[j][i-1]][i-1]); //w[j][i] 为 j 向上走 2^i 个节点经过路径的权值最小值
    } while(q--) printf("%d
",LCA(read(),read()));	
	return 0;
}

以上是关于P1967 货车运输 题解的主要内容,如果未能解决你的问题,请参考以下文章

Luogu P1967 货车运输题解

luogu P1967 货车运输

P1967 货车运输

P1967 货车运输

P1967 货车运输

[luogu P1967][NOIp2013]P1967 货车运输