[BJWC2010] 严格次小生成树(kruskal+树剖)

2023-05-16

这题果然是模板题

一堆做法

但是根本思想是一样的

都是先跑一遍最小生成树,然后维护一下路径上最大值和小于最大值的最大值

主要的实现方法有三种

1.kruskal+倍增+lca

复杂度是 O ( m l o g m ) O(mlogm) O(mlogm),优点是复杂度低,常熟不是特别大,代码短,缺点是实现细节多

2.kruskal+lct

复杂度还是 O ( m l o g m ) O(mlogm) O(mlogm),优点是复杂度低,代码长度较短,缺点是常熟大,实现细节多

3.kruskal+树剖

复杂度是 O ( m l o g 2 n ) O(mlog^2n) O(mlog2n),优点是实现简单(只用考虑左右儿子的合并),细节少,(在同复杂度水平下)常熟小,缺点是复杂度有点高,代码长度大,但是这道题 1 0 5 10^5 105级别的数据复杂度是正确的

所以我们主要来说一下这个树剖的做法(自认为是最稳的一种)

先跑一遍最小生成树,然后线段树维护最小生成树上的区间最大值和区间小于最大值的最大值

那么pushup大概是这个样子的

void pushup(int u){
	if(seg[lc]._max==seg[rc]._max)seg[u]._max=seg[lc]._max,seg[u].__max=max(seg[lc].__max,seg[rc].__max);
	else seg[u]._max=max(seg[lc]._max,seg[rc]._max),seg[u].__max=max(max(seg[lc].__max,seg[rc].__max),min(seg[lc]._max,seg[rc]._max));	
}

其中_max是最大值,__max是小于最大值的最大是变量名毒瘤勿喷

解释一下就是如果左右儿子的最大值相等,那么最大值就是这个值,次大值就是左右儿子的次大值中更大的那个

如果不等,最大值就是左右儿子中最大值较大的那个,次大值就是左右儿子中最大值较小的那个,或者是两个次大值中更大的那个,这两种情况去一个max(一定要注意取max要不然虽然能A但是是错的

维护好这个之后,我们每次尝试加进去一条不是最小生成树上的边,这样会形成一个环,然后如果环上最大的那条边 ≥ \geq 要加进去的这条边就看第二大能不能加,然后算出来这条边加进去之后的最小生成树,最后统一取个min就可以了

几个问题

1.要开longlong,ans初值要大一点,否则wa#8

2.题目只说保证没有重边,没说保证没有自环,所以判断一下,否则wa#10

3.数组开的稍微大一点,否则re#10qwq

代码:

# include <bits/stdc++.h>
using namespace std;

# define Rep(i,a,b) for(int i=a;i<=b;i++)
# define _Rep(i,a,b) for(int i=a;i>=b;i--)
# define RepG(i,u) for(int i=head[u];~i;i=e[i].next)
# define debug puts("QAQ");

typedef long long ll;
const int N=2e5+5;
const int mod=1e9+7;
const double eps=1e-7;

template <typename T> void read(T &x){
	x=0;int f=1;
	char c=getchar();
	for(;!isdigit(c);c=getchar())if(c=='-')f=-1;
	for(;isdigit(c);c=getchar())x=(x<<1)+(x<<3)+c-'0';
	x*=f;
}

# define int long long

int n,m;
int head[N],cnt;
int faz[N],son[N],dep[N],siz[N],top[N],dfn[N],tot;
int sum,ans=1e18;
int val[N],_a[N];
int f[N];
bool vis[N];

struct Edge{
	int to,next,w;	
}e[N<<1];

void add(int x,int y,int c){
	e[++cnt]=(Edge){y,head[x],c},head[x]=cnt;	
}

struct edge{
	int x,y,c;
	bool operator < (const edge &cmp)const{
		return c<cmp.c;
	}
}a[N<<2];

int find(int x){
	if(f[x]==x)return x;
	return f[x]=find(f[x]);	
}

struct segment_tree{
	int l,r;
	int _max,__max;	
}seg[N<<2];

# define lc (u<<1)
# define rc (u<<1|1)

void pushup(int u){
	if(seg[lc]._max==seg[rc]._max)seg[u]._max=seg[lc]._max,seg[u].__max=max(seg[lc].__max,seg[rc].__max);
	else seg[u]._max=max(seg[lc]._max,seg[rc]._max),seg[u].__max=max(max(seg[lc].__max,seg[rc].__max),min(seg[lc]._max,seg[rc]._max));	
}

pair<int,int> merge(pair<int,int> l,pair<int,int> r){
	pair<int,int> res;
	if(l.first==r.first)res.first=l.first,res.second=max(l.second,r.second);
	else res.first=max(l.first,r.first),res.second=max(max(l.second,r.second),min(l.first,r.first));
	return res;	
}

void build(int u,int l,int r){
	seg[u].l=l,seg[u].r=r;
	if(l==r){seg[u]._max=_a[l];return;}
	int mid=l+r>>1;
	build(lc,l,mid);
	build(rc,mid+1,r);
	pushup(u);
}

pair<int,int> query(int u,int l,int r){
	if(seg[u].l>=l&&seg[u].r<=r)return make_pair(seg[u]._max,seg[u].__max);
	int mid=seg[u].l+seg[u].r>>1;
	if(r<=mid)return query(lc,l,r);
	if(l>mid)return query(rc,l,r);
	return merge(query(lc,l,r),query(rc,l,r));	
}

pair<int,int> RouteQuery(int x,int y){
	pair<int,int> res;
	while(top[x]!=top[y]){
		if(dep[top[x]]<dep[top[y]])swap(x,y);
		res=merge(res,query(1,dfn[top[x]],dfn[x]));
		x=faz[top[x]];
	}
	if(x!=y){
		if(dep[x]>dep[y])swap(x,y);
		res=merge(res,query(1,dfn[x]+1,dfn[y]));	
	}
	return res;
}

void dfs1(int u,int fa){
	faz[u]=fa;
	siz[u]=1;
	dep[u]=dep[fa]+1;
	RepG(i,u){
		int v=e[i].to;
		if(v==fa)continue;
		val[v]=e[i].w;
		dfs1(v,u);
		siz[u]+=siz[v];
		if(siz[v]>siz[son[u]])son[u]=v;	
	}
}

void dfs2(int u,int _top){
	top[u]=_top;
	dfn[u]=++tot;
	_a[tot]=val[u];	
	if(!son[u])return;
	dfs2(son[u],_top);
	RepG(i,u){
		int v=e[i].to;
		if(v==faz[u]||v==son[u])continue;
		dfs2(v,v);	
	}
}

void kruskal(){
	int tott=0;
	sort(a+1,a+m+1);
	Rep(i,1,m){
		int fax=find(a[i].x),fay=find(a[i].y);
		if(fax==fay)continue;
		vis[i]=true;
		f[fax]=fay;
		add(a[i].x,a[i].y,a[i].c),add(a[i].y,a[i].x,a[i].c);
		sum+=a[i].c;
		if(++tott==n-1)break;
	}
}

signed main()
{
	memset(head,-1,sizeof(head));
	read(n),read(m);
	Rep(i,1,n)f[i]=i;
	Rep(i,1,m){
		read(a[i].x),read(a[i].y),read(a[i].c);
		if(a[i].x==a[i].y)vis[i]=true;
	}
	kruskal();	
	dfs1(1,0),dfs2(1,1);
	build(1,1,n);
	Rep(i,1,m){
		if(vis[i])continue;
		int x=a[i].x,y=a[i].y,c=a[i].c;
		pair<int,int> res=RouteQuery(x,y);
		if(res.first<c)ans=min(ans,sum+c-res.first);
		else if(res.second&&res.second<c)ans=min(ans,sum+c-res.second);
	}
	printf("%lld\n",ans);
	return 0;
}

因为没有修改问题所以代码也不是很长,160+

update 2020.2.29 13:57 这题数据有点水,开始pushup写错了居然还A了

一定要注意考虑次大的也可能是最大的

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

[BJWC2010] 严格次小生成树(kruskal+树剖) 的相关文章

  • python运算符&用法的详细介绍

    目录 1 算数运算 2 比较运算符 3 成员运算符 4 逻辑运算 5 赋值运算 附 xff1a 类型转换 1 算数运算 运算符 xff1a 43 加 减 乘 除 整除 余数 幂运算 多用于整数 浮点数进行计算 43 也可用于字符串 xff0
  • 第三篇 树莓派的串口通信和语音识别模块

    目录 一 串口 xff08 UART xff09 二 wiringPi提供的串口API 三 语音识别模块 1 阅读模块代码 代码阅读工具 xff1a Souces Insight4 0安装 激活 汉化等 语音识别 xff08 口令模式 xf
  • 安装配置 JupyterLab ubuntu20.04

    目录 编辑 xff08 1 xff09 安装 xff08 2 xff09 配置 xff08 1 xff09 生成配置文件 xff08 2 xff09 生成jupyterlab的登录密码 xff08 3 xff09 修改 jupyter 的配
  • 笔记本安装双系统(win11+centos7)自己遇到坑的总结

    笔记本安装CentOS操作系统 当初在学习CentOS7的时候 就想在自己的笔记本上装一个CentOS7 装CentOS7和Windows双系统 xff0c 安装的过程中也查阅很多资料但是都不是很齐全 xff0c 我将自己安装的全过程总结出
  • 平衡树·splay

    文章目录 1 About splay2 基本操作2 1 数组是干啥的 xff1f 2 2 基本操作 3 splay3 1 rotate函数3 2 splay函数 4 更新操作4 1 插入函数4 2 删除函数 5 查询操作5 1 查询一个数的
  • 删除双系统中的Linux系统(总结以及恢复U盘原样)

    一丶如何删除双系统中的Linux系统 xff08 这里的双系统是win 43 Linux xff09 第一步 首先打开磁盘管理器 xff0c 将要删除的Linux系统的主分区右键点击删除 xff0c 删除后就可以关闭磁盘管理器 第二步 在电
  • centos7安装docker

    一丶先了解下 xff0c 什么是 Docker xff1f 为什么会有 Docker xff1f Docker 的优势 xff1f docker的组成 xff1f 1 为什么会有 Docker xff1f 我们知道一款产品从开发到上线 xf

随机推荐