• 【bzoj1977】[BeiJing2010组队]次小生成树 Tree 最小生成树+权值线段树合并


    题目描述

    求一张图的严格次小生成树的边权和,保证存在。

    输入

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

    输出

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

    样例输入

    5 6
    1 2 1
    1 3 2
    2 4 3
    3 5 4
    3 4 3
    4 5 6

    样例输出

    11


    题解

    最小生成树+权值线段树合并

    首先有一个常用的结论:次小生成树(无论是否严格)只要存在,则一定可以由最小生成树仅改变一条边构成,并且添加的边一定能覆盖删除的边。

    然后考虑删除哪条边或者加入哪条边均可。

    然后只会写数据结构的傻逼GXZ的做法比较naive:考虑每一条非树边的贡献,给两个端点打加入标记,给LCA打删除标记,自底向上跑权值线段树合并,维护出现过的最小权值和次小权值,然后搜到每条边时用最小的不相等的权值更新答案。

    正解貌似是考虑加入哪条非树边,倍增找最小次小值?不管了反正512MB内存能过。。。

    时间复杂度$O(nlog n)$

    #include <cstdio>
    #include <cstring>
    #include <algorithm>
    #define N 100010
    #define M 1000000000
    #define inf 0x7f7f7f7f
    using namespace std;
    struct data
    {
    	int x , y , z;
    	bool operator<(const data &a)const {return z < a.z;}
    }a[N * 3];
    int f[N] , flag[N * 3] , head[N] , to[N << 1] , len[N << 1] , next[N << 1] , cnt , fa[N][20] , deep[N] , val[N] , log[N];
    int ls[N << 7] , rs[N << 7] , si[N << 7] , mx[N << 7] , sx[N << 7] , tot , root[N] , ans = inf;
    int find(int x)
    {
    	return x == f[x] ? x : f[x] = find(f[x]);
    }
    inline void add(int x , int y , int z)
    {
    	to[++cnt] = y , len[cnt] = z , next[cnt] = head[x] , head[x] = cnt;
    }
    void dfs(int x)
    {
    	int i;
    	for(i = 1 ; (1 << i) <= deep[x] ; i ++ ) fa[x][i] = fa[fa[x][i - 1]][i - 1];
    	for(i = head[x] ; i ; i = next[i])
    		if(to[i] != fa[x][0])
    			fa[to[i]][0] = x , deep[to[i]] = deep[x] + 1 , val[to[i]] = len[i] , dfs(to[i]);
    }
    inline int lca(int x , int y)
    {
    	int i;
    	if(deep[x] < deep[y]) swap(x , y);
    	for(i = log[deep[x] - deep[y]] ; ~i ; i -- )
    		if(deep[x] - deep[y] >= (1 << i))
    			x = fa[x][i];
    	if(x == y) return x;
    	for(i = log[deep[x]] ; ~i ; i -- )
    		if(deep[x] >= (1 << i) && fa[x][i] != fa[y][i])
    			x = fa[x][i] , y = fa[y][i];
    	return fa[x][0];
    }
    void pushup(int x)
    {
    	if(mx[ls[x]] == inf) mx[x] = mx[rs[x]] , sx[x] = sx[rs[x]];
    	else if(sx[ls[x]] == inf) mx[x] = mx[ls[x]] , sx[x] = mx[rs[x]];
    	else mx[x] = mx[ls[x]] , sx[x] = sx[ls[x]];
    }
    void update(int p , int a , int l , int r , int &x)
    {
    	if(!x) x = ++tot;
    	if(l == r)
    	{
    		si[x] += a;
    		if(si[x] > 0) mx[x] = l;
    		else mx[x] = inf;
    		return;
    	}
    	int mid = (l + r) >> 1;
    	if(p <= mid) update(p , a , l , mid , ls[x]);
    	else update(p , a , mid + 1 , r , rs[x]);
    	pushup(x);
    }
    int merge(int l , int r , int x , int y)
    {
    	if(!x) return y;
    	if(!y) return x;
    	if(l == r)
    	{
    		si[x] += si[y];
    		if(si[x] > 0) mx[x] = l;
    		else mx[x] = inf;
    		return x;
    	}
    	int mid = (l + r) >> 1;
    	ls[x] = merge(l , mid , ls[x] , ls[y]);
    	rs[x] = merge(mid + 1 , r , rs[x] , rs[y]);
    	pushup(x);
    	return x;
    }
    void solve(int x)
    {
    	int i;
    	for(i = head[x] ; i ; i = next[i])
    		if(to[i] != fa[x][0])
    			solve(to[i]) , root[x] = merge(1 , M , root[x] , root[to[i]]);
    	if(mx[root[x]] != inf)
    	{
    		if(mx[root[x]] != val[x]) ans = min(ans , mx[root[x]] - val[x]);
    		else if(sx[root[x]] != inf) ans = min(ans , sx[root[x]] - val[x]);
    	}
    }
    int main()
    {
    	int n , m , i;
    	long long sum = 0;
    	scanf("%d%d" , &n , &m);
    	for(i = 1 ; i <= m ; i ++ ) scanf("%d%d%d" , &a[i].x , &a[i].y , &a[i].z);
    	sort(a + 1 , a + m + 1);
    	log[0] = -1;
    	for(i = 1 ; i <= n ; i ++ ) f[i] = i , log[i] = log[i >> 1] + 1;
    	for(i = 1 ; i <= m ; i ++ )
    		if(find(a[i].x) != find(a[i].y))
    			f[f[a[i].x]] = f[a[i].y] , add(a[i].x , a[i].y , a[i].z) , add(a[i].y , a[i].x , a[i].z) , sum += a[i].z , flag[i] = 1;
    	dfs(1);
    	memset(mx , 0x7f , sizeof(mx)) , memset(sx , 0x7f , sizeof(sx));
    	for(i = 1 ; i <= m ; i ++ )
    		if(!flag[i])
    			update(a[i].z , 1 , 1 , M , root[a[i].x]) , update(a[i].z , 1 , 1 , M , root[a[i].y]) , update(a[i].z , -2 , 1 , M , root[lca(a[i].x , a[i].y)]);
    	solve(1);
    	printf("%lld
    " , sum + ans);
    	return 0;
    }
    

     

  • 相关阅读:
    win7系统中如何使文件显示出扩展名
    source insight
    9-mwwtj-2r6fk-xeu7c-cj6em-asm6m
    修改palceholder内文字的css样式
    移动端页面默认样式重置
    classpath路径
    在Windows系统里创建.gitignore文件
    MessageDigest
    Integer.toHexString(byte & 0xFF)
    jQuery的extend方法
  • 原文地址:https://www.cnblogs.com/GXZlegend/p/7718442.html
Copyright © 2020-2023  润新知