zoukankan      html  css  js  c++  java
  • P4180 【模板】严格次小生成树[BJWC2010]

    P4180 【模板】严格次小生成树[BJWC2010]

    题目描述
    小C最近学了很多最小生成树的算法,Prim算法、Kurskal算法、消圈算法等等。正当小C洋洋得意之时,小P又来泼小C冷水了。小P说,让小C求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小的,也就是说:如果最小生成树选择的边集是EM,严格次小生成树选择的边集是ES,那么需要满足:(value(e)表示边e的权值) (sum_{e in E_M}value(e)<sum_{e in E_S}value(e))
    这下小 C 蒙了,他找到了你,希望你帮他解决这个问题。

    输入输出格式
    输入格式:
    第一行包含两个整数N 和M,表示无向图的点数与边数。 接下来 M行,每行 3个数x y z 表示,点 x 和点y之间有一条边,边的权值为z。

    输出格式:
    包含一行,仅一个数,表示严格次小生成树的边权和。(数据保证必定存在严格次小生成树)


    首先明确严格, 指的是次小生成树边权之和小于而不是小于等于最小生成树
    搞清楚这点, 我们就有了一个大概思路: 先构造一个最小生成树, 枚举每一条没选上的边, 查询这条边连接的两点之间在最小生成树上的最大值和次大值(次大值防止出现等于的情况, 严格), 进行替换, 维护答案最小即可。

    首先想到树剖, 发现线段树维护次大值较为繁琐且查询复杂度不够理想, 常熟较大。

    考虑倍增 (LCA) ,在倍增初始化时预处理出一段链上(具体是从当前点向上跳 (2^{i}) 个点的这条链上)的最大、次大值。因为树上两点间有且仅有一条路径且此路径通过他们的 (LCA) ,故查询最大、 次大值时, 分别倍增查询两个节点到其 (LCA) 的最大值即可

    查询过程中注意严格这一定义

    复杂度:(O(Kruskal) + O(nlog n))

    Code

    #include<iostream>
    #include<cstdio>
    #include<queue>
    #include<cstring>
    #include<algorithm>
    typedef long long LL;
    using namespace std;
    LL RD(){
        LL out = 0,flag = 1;char c = getchar();
        while(c < '0' || c >'9'){if(c == '-')flag = -1;c = getchar();}
        while(c >= '0' && c <= '9'){out = out * 10 + c - '0';c = getchar();}
        return flag * out;
        }
    const LL maxn = 300019,INF = 0xfffffffffffffff;
    LL head[maxn],nume = 1;
    struct Node{
        LL v,dis,nxt;
        }E[maxn << 3];
    void add(LL u,LL v,LL dis){
        E[++nume].nxt = head[u];
        E[nume].v = v;
        E[nume].dis = dis;
        head[u] = nume;
        }
    LL num, nr;
    LL father[maxn];
    LL findfather(LL v){
    	if(father[v] == v)return v;
    	return father[v] = findfather(father[v]);
    	}
    struct Edge{
    	LL u, v, dis;
    	bool used;
    	}I[maxn];
    bool cmp(Edge a, Edge b){return a.dis < b.dis;}
    LL tot, root;
    LL jump[maxn][25],dep[maxn];
    LL maxx[maxn][25], mx[maxn][25];
    void dfs(LL u, LL F){
    	for(LL i = head[u];i;i = E[i].nxt){
    		LL v = E[i].v;
    		if(v == F)continue;
    		jump[v][0] = u;
    		dep[v] = dep[u] + 1;
    		maxx[v][0] = E[i].dis;
    		mx[v][0] = -INF;
    		dfs(v, u);
    		}
    	}
    void get_jump(LL s){
    	dep[s] = 1;
    	dfs(s, -1);
    	for(LL i = 1;i <= 19;i++){
    		for(LL j = 1;j <= num;j++){
    			LL F = jump[j][i - 1];
    			jump[j][i] = jump[F][i - 1];
    			maxx[j][i] = max(maxx[j][i - 1], maxx[F][i - 1]);
    			mx[j][i] = max(mx[j][i - 1], mx[F][i - 1]);
    			if(maxx[j][i] != maxx[j][i - 1])mx[j][i] = max(mx[j][i], maxx[j][i - 1]);
    			else mx[j][i] = max(mx[j][i], maxx[F][i - 1]);
    			}
    		}
    	}
    LL LCA(LL x, LL y){
    	if(dep[x] < dep[y])swap(x, y);
    	for(LL i = 19;i >= 0;i--)if(dep[jump[x][i]] >= dep[y])x = jump[x][i];
    	if(x == y)return x;
    	for(LL i = 19;i >= 0;i--)if(jump[x][i] != jump[y][i])x = jump[x][i], y = jump[y][i];
    	return jump[x][0];
    	}
    LL get_max(LL x, LL y, LL MAX){
    	LL lca = LCA(x, y);
    	LL ans = -INF;
    	for(LL i = 19;i >= 0;i--){
    		if(dep[jump[x][i]] >= dep[lca]){
    			if(maxx[x][i] < MAX)ans = max(ans, maxx[x][i]);
    			else ans = max(ans, mx[x][i]);
    			x = jump[x][i];
    			}
    		}
    	for(LL i = 19;i >= 0;i--){
    		if(dep[jump[y][i]] >= dep[lca]){
    			if(maxx[y][i] < MAX)ans = max(ans, maxx[y][i]);
    			else ans = max(ans, mx[y][i]);
    			y = jump[y][i];
    			}
    		}
    	return ans;
    	}
    int main(){
    	num = RD();nr = RD();
    	for(LL i = 1;i <= num;i++)father[i] = i;
    	for(LL i = 1;i <= nr;i++)I[i].u = RD(), I[i].v = RD(), I[i].dis = RD();
    	sort(I + 1, I + 1 + nr, cmp);
    	for(LL i = 1;i <= nr;i++){
    		LL faA = findfather(I[i].u), faB = findfather(I[i].v);
    		if(faA != faB){
    			root = I[i].u;
    			father[faA] = faB, tot += I[i].dis;
    			add(I[i].u, I[i].v, I[i].dis);
    			add(I[i].v, I[i].u, I[i].dis);
    			I[i].used = 1;
    			}
    		}
    	get_jump(root);
    	LL ans = INF;
    	for(LL i = 1;i <= nr;i++){
    		if(!I[i].used){
    			LL now = get_max(I[i].u, I[i].v, I[i].dis);
    			ans = min(ans, tot - now + I[i].dis);
    			}
    		}
    	printf("%lld
    ", ans);
    	return 0;
    	}
    
  • 相关阅读:
    洛谷P1656 炸铁路 题解 并查集
    洛谷P1455 搭配购买 题解 并查集+01背包
    欧拉计划第16题题解
    欧拉计划第15题题解
    欧拉计划第14题题解
    scrapy 爬虫保存数据
    FileNotFoundError: File b'score.xlsx' does not exist
    图片截取
    图像过滤器
    如何查看字符编码?
  • 原文地址:https://www.cnblogs.com/Tony-Double-Sky/p/9402271.html
Copyright © 2011-2022 走看看