[luoguP1967]货车运输
我做这道题可以说做的心力交瘁,累计做题2天半,重构代码1次,调试 无数次 , 面向数据编程 ,不过总算是过了
思路很清楚,题解很多,是最大生成树 + 倍增lca求路径最小边权
下面主要总结一下经验教训:
一. 最大生成树的算法选择 和 原图未知连通性、环 的考虑问题
题中的图非常随意,可能有环,也可能有相互不连通的两个甚至多个连通分量。这种情况下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\n");
else
printf("%lld\n",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;
}