[luoguP1967]货车运输

原题

我做这道题可以说做的心力交瘁,累计做题2天半,重构代码1次,调试 无数次 , 面向数据编程 ,不过总算是过了

思路很清楚,题解很多,是最大生成树 + 倍增lca求路径最小边权

下面主要总结一下经验教训:

一. 最大生成树的算法选择 和 原图未知连通性、环 的考虑问题

算法选择
图中文字源于:stackoverflow,侵删

题中的图非常随意,可能有环,也可能有相互不连通的两个甚至多个连通分量。这种情况下kruskal仍然可用(最小改成最大就好了),但注意添加的边权数应为(边数 - 相互不连通的连通分量数)

其中一条无向边看作一条边计数

这样得到的最大生成林没有环
另外注意相同连通分量的每个点用dfs标号(可看作一个并查集),求最大生成树时如果用Kruskal应用另外一个并查集。因为一条边两端的点一定属于同一个连通分量,所以不必更改kruskal中有关并查集的操作

二. 倍增lca 协助 求边权最小值的应用

假设f[node][times]为node向上倍增2(times)的祖先,那我们同样可以设d[node][times]为node向上倍增2(times)经过的边中边权的最小值,可得转移方程为 d[node][times] = min(d[node][times - 1] ,d[ f[node][times-1] ][times-1] ) (times >= 1 , f[node][times-1] > 0 && d[f[node][times-1][times-1] > 0)
方程仅供参考

注意lca递推、边权最小值递推和下一层递归的顺序,应该先递推再递归(……有点绕口了)

三. 其他细节

在求两个点的lca的同时求最小边权时,应在追求两点深度相同、两点一同跃至lca、求得lca时都取一遍d[][]和ans的最小赋给ans,并注意求答案的位置

四. 写正解的弊端 和 暴搜的优越性

对于博主这种水平的蒟蒻来说,一次写正解很难写对,耗费大量时间,搞不好还会全部WA,五分莫得。而写爆搜却能在一定程度上保证答案的正确性,尽可能避免翻车的同时尽量拿到分数保底,是比赛时一种比较稳当的策略。

源码如下,如有错误敬请斧正

#include <algorithm>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <queue>
typedef long long ll;
const ll MAX = 0x3f3f3f3f;
const ll MAXN = 10010;
const ll MAXM = 50010;
struct zNode {
	ll x,y,d,next;
};
zNode e[MAXM<<1],e2[MAXM<<1];
ll head[MAXN],count,head2[MAXN],count2;
ll cont[MAXN],new_root[MAXN],ques[30010][2];
ll depth[MAXN],f[MAXN][100],dis[MAXN][100];
ll n,m,q,mst_num;
ll find(ll);
ll find_lca(ll,ll);
ll s_find_lca(ll,ll);
void dfs(ll,ll);
void insert(ll,ll,ll);
void insert2(ll,ll,ll);
void input_affairs();
void connection_check();
void tree_building();
void dfs_lca(ll);
void query_affairs();
bool cmp_tre(zNode,zNode);
int main() {
	input_affairs();
	connection_check();
	tree_building();
	for(ll node = 1 ; node <= n ; ++node ) {
		if(depth[node] == 0) {
			depth[node] = 1;
			dfs_lca(node);
		}
	}
	query_affairs();
	return 0;
}
void query_affairs() {
	for(ll order = 1 ; order <= q ; ++order ) {
		if(cont[ques[order][0]] != cont[ques[order][1]])
			printf("-1
");
		else
			printf("%lld
",s_find_lca(ques[order][0],ques[order][1]));
	}
}
ll s_find_lca(ll x , ll y) {
	if(cont[x] != cont[y]) return -1;
	if(x == y) return x;
	if(depth[x] > depth[y])
		std::swap(x,y);
	ll limit = std::log(depth[y]) / std::log(2) + 1;
	ll ans = ll(1e15),ground = 0;
	if(depth[x] != depth[y]) {
		for(ground = limit ; ground >= 0 ; --ground) {
			if(depth[f[y][ground]] >= depth[x]) {
				if(dis[y][ground] > 0)
					ans = std::min(ans , dis[y][ground]);
				y = f[y][ground];
			}
		}
	}
	if(x == y) return ans;
	for(ground = limit ; ground >= 0 ; --ground) {
		if(f[x][ground] != f[y][ground]) { 
			if(dis[x][ground] > 0)
				ans = std::min(ans , dis[x][ground]);
			if(dis[y][ground] > 0)
				ans = std::min(ans , dis[y][ground]);
			x = f[x][ground] , y = f[y][ground];
		}
	}
	if(dis[x][0] > 0)
		ans = std::min(ans , dis[x][0]);
	if(dis[y][0] > 0) 
		ans = std::min(ans , dis[y][0]);
	return ans;
}
void dfs_lca(ll node) { 
	for(ll index = head2[node] ; index ; index = e2[index].next ) {
		const ll& son_node = e2[index].y;
		ll limit = std::log(depth[node]) / std::log(2) + 1;
		if(depth[son_node] == 0 ) {
			depth[son_node] = depth[node] + 1;
			limit = std::log(depth[son_node]) / std::log(2) + 1;
			f[son_node][0] = node;
			dis[son_node][0] = e2[index].d;

			for(ll times = 1 ; times <= limit && f[son_node][times-1] > 0; ++times) {
				f[son_node][times] = f[f[son_node][times-1]][times-1];
			}
			for(ll times = 1 ; times <= limit && dis[f[son_node][times-1]][times-1] > 0; ++times) {
				dis[son_node][times] =  std::min(dis[son_node][times-1],dis[f[son_node][times-1]][times-1]) ;
			}
			dfs_lca(son_node);
		}
	}
}
bool cmp_tre(zNode z1,zNode z2) {
	return z1.d > z2.d;
}
ll find(ll x) {
	while(new_root[x] != x) {
		new_root[x] = new_root[new_root[x]] ;
		x = new_root[x];
	}
	return new_root[x];
}
void tree_building() { 
	for(ll node = 1 ; node <= n ; ++node)
		new_root[node] = node;
	std::sort(e + 1 , e + 1 + count , cmp_tre);
	ll added = 0 ,temp = 0;
	for(ll pos = 1 ; pos <= count && added < n-mst_num  ; ++ pos) {
		if(find(e[pos].x) != find(e[pos].y)) {
			temp = find(e[pos].x);
			new_root[temp] = e[pos].y;
			insert2(e[pos].x,e[pos].y,e[pos].d);
			insert2(e[pos].y,e[pos].x,e[pos].d);
			++added;
		}
	}
}
void connection_check() {
	ll cnt = 0;
	for(ll i = 1 ;  i <= n ; ++i) {
		if(cont[i] == 0) {
			++cnt;
			cont[i] = cnt;
			dfs(i,cnt);
		}
	}
	mst_num = cnt;
}
void input_affairs() {
	scanf("%lld%lld",&n,&m);
	for(ll i = 1 ; i <= m ; ++i) {
		ll x,y,z;
		scanf("%lld%lld%lld",&x,&y,&z);
		insert(x,y,z);
		insert(y,x,z);
	}
	scanf("%lld",&q);
	for(ll i = 1; i <= q ; ++i) {
		scanf("%lld%lld",&ques[i][0],&ques[i][1]);
	}
}
void dfs(ll node,ll mark) {
	for(ll x = head[node] ; x ; x = e[x].next) {
		ll& temp = e[x].y;
		if(cont[temp] != 0)
			continue;
		cont[temp] = mark;
		dfs(temp,mark);
	}
}
void insert(ll u,ll v,ll d) {
	++count;
	e[count].x = u;
	e[count].y = v;
	e[count].d = d;
	e[count].next = head[u];
	head[u] = count;
}
void insert2(ll u,ll v,ll d) {
	++count2;
	e2[count2].x = u;
	e2[count2].y = v;
	e2[count2].d = d;
	e2[count2].next = head2[u];
	head2[u] = count2;
}
原文地址:https://www.cnblogs.com/StarOnTheWay/p/11237020.html