zoukankan      html  css  js  c++  java
  • [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;
    }
    
  • 相关阅读:
    VMware虚拟机安装红帽系统无法上网解决办法(转)
    二维指针的malloc内存分配(转)
    c语言中如何通过二级指针来操作二维数组
    如何把一个二维数组的地址赋给一个二维指针?
    二维数组及二维指针的传递及一些思考(转)
    js怎么让时间函数的秒数在页面上显示是变化的
    jquery 实现各种统计图网址
    WEB的相关知识总结
    JS同名方法,
    web components思想如何应用于实际项目
  • 原文地址:https://www.cnblogs.com/StarOnTheWay/p/11237020.html
Copyright © 2011-2022 走看看