zoukankan      html  css  js  c++  java
  • P4180-[BJWC2010]严格次小生成树【Kruskal,倍增】

    正题

    题目链接:https://www.luogu.com.cn/problem/P4180


    题目大意

    (n)个点(m)条边的一张无向图,求它的严格次小生成树。

    (1leq nleq 10^5,1leq mleq 3 imes 10^5)


    解题思路

    一定存在一种严格次小生成树和最小生成树只差一条边,感性理解的话大概就是如果有两条不同那么肯定有一条可以替换成另一条要么更优要么不变。

    所以我们可以枚举一条不选的边((u,v,w))然后找到最小生成树上(u,v)路径最大的权值(k)替换。

    但是发现有可能恰好(w=k),所以我们不只需要统计最大的权值,还要统计一个严格次大的,如果等于就选择严格次大的。

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


    code

    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #define ll long long
    using namespace std;
    const ll N=1e5+10,T=18;
    struct enode{
    	ll x,y,w,v;
    }e[3*N];
    struct node{
    	ll to,next,w;
    }a[N<<1];
    ll n,m,tot,ls[N],fa[N],dep[N];
    ll ans,sum,f[N][T],g[N][T][2];
    bool cmp(enode x,enode y)
    {return x.w<y.w;}
    ll find(ll x)
    {return (fa[x]==x)?x:(fa[x]=find(fa[x]));}
    void addl(ll x,ll y,ll w){
    	a[++tot].to=y;
    	a[tot].next=ls[x];
    	ls[x]=tot;a[tot].w=w;
    	return;
    }
    void dfs(ll x,ll fa){
    	dep[x]=dep[fa]+1;
    	for(ll i=ls[x];i;i=a[i].next){
    		ll y=a[i].to;
    		if(y==fa)continue;
    		f[y][0]=x;g[y][0][0]=a[i].w;
    		dfs(y,x);
    	}
    	return;
    }
    void calc(ll &mx,ll &mi,ll x){
    	if(x>mx)mi=mx,mx=x;
    	else if(x>mi&&x!=mx)mi=x;
    	return;
    }
    void calccc(ll x,ll y,ll w){
    	if(x==y)return;
    	if(dep[x]>dep[y])swap(x,y);
    	ll mx=-1,mi=-1;
    	for(ll i=T-1;i>=0;i--)
    		if(dep[f[y][i]]>=dep[x]){
    			calc(mx,mi,g[y][i][0]);
    			calc(mx,mi,g[y][i][1]);
    			y=f[y][i];
    		}
    	if(x!=y){
    		for(ll i=T-1;i>=0;i--)
    			if(f[x][i]!=f[y][i]){
    				calc(mx,mi,g[x][i][0]);
    				calc(mx,mi,g[x][i][1]);
    				calc(mx,mi,g[y][i][0]);
    				calc(mx,mi,g[y][i][1]);
    				x=f[x][i];y=f[y][i];
    			}
    		calc(mx,mi,g[x][0][0]);
    		calc(mx,mi,g[x][0][1]);
    		calc(mx,mi,g[y][0][0]);
    		calc(mx,mi,g[y][0][1]);
    	}
    	if(w!=mx)ans=min(ans,sum+w-mx);
    	else if(mi>=0)ans=min(ans,sum+w-mi);
    	return;
    }
    signed main()
    {
    	scanf("%lld%lld",&n,&m);
    	for(ll i=1;i<=m;i++)
    		scanf("%lld%lld%lld",&e[i].x,&e[i].y,&e[i].w);
    	sort(e+1,e+1+m,cmp);
    	for(ll i=1;i<=n;i++)fa[i]=i;
    	for(ll i=1;i<=m;i++){
    		ll x=find(e[i].x),y=find(e[i].y);
    		if(x==y)continue;
    		addl(e[i].x,e[i].y,e[i].w);
    		addl(e[i].y,e[i].x,e[i].w);
    		fa[x]=y;e[i].v=1;sum+=e[i].w;
    	}
    	memset(g,-1,sizeof(g));
    	dfs(1,1);
    	for(ll j=1;j<T;j++)
    		for(ll i=1;i<=n;i++){
    			f[i][j]=f[f[i][j-1]][j-1];
    			calc(g[i][j][0],g[i][j][1],g[i][j-1][0]);
    			calc(g[i][j][0],g[i][j][1],g[i][j-1][1]);
    			calc(g[i][j][0],g[i][j][1],g[f[i][j-1]][j-1][0]);
    			calc(g[i][j][0],g[i][j][1],g[f[i][j-1]][j-1][1]);
    		}
    	ans=1e18;
    	for(ll i=1;i<=m;i++)
    		if(!e[i].v)calccc(e[i].x,e[i].y,e[i].w);
    	printf("%lld
    ",ans);
    	return 0;
    }
    
  • 相关阅读:
    day7 面向对象 静态方法 类方法 属性方法 类的特殊成员方法 元类 反射 异常处理
    day6 面向对象 封装 继承 多态 类与实例在内存中的关系 经典类和新式类
    day5 time datetime random os sys shutil json pickle shelve xml configparser hashlib subprocess logging re正则 python计算器
    kafka常用操作命令
    linux基础
    django学习1——初识web应用程序
    mysql数据库(三)——pymysql模块
    mysql数据库(二)——表的查询
    mysql数据库(一)
    Python常用模块——re模块
  • 原文地址:https://www.cnblogs.com/QuantAsk/p/15021981.html
Copyright © 2011-2022 走看看