ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

AcWing 356. 次小生成树

2022-07-10 20:36:37  阅读:141  来源: 互联网

标签:ch int mx2 tr 生成 356 mx1 mx AcWing


分析

这题做法很简单:

  • 跑一遍 \(\texttt{MST}\)(最小生成树),把这棵树建立起来,上面的边标记为树边。

  • 枚举非树边 \((u, v)\),记边权为 \(w\),考虑这条边能够提供的增量 \(del\)。

    具体来说:只需要求出树上 \(u\to v\) 的路径上的边的最大值 \(mx_1\) 和严格次大值 \(mx_2\)(也就是保证 \(mx_2 < mx_1\)),假如 \(w>mx_1\),那我们就更新 \(del\to \min(del, w-mx_1)\),如果 \(w=mx_1\),就更新 \(del\to \min(del, w-mx_2)\).

上面的思路简单来说就是枚举非树边,加入满足替换后增量最小的边以得到答案。

实现

关键在于使用什么样的数据结构维护树上路径的最大值与次大值。

这里采用了线段树 + 树链剖分:

线段树的每个节点维护了一个最大值与次大值,这两个信息都是可以合并的,\(\texttt{merge}\) 函数见下:

Node merge(Node &L, Node &R){
	int rec[]={L.mx1, L.mx2, R.mx1, R.mx2};
	int mx1=-INF, mx2=-INF;
	for(auto i: rec) mx1=max(mx1, i); 
	rep(i,0,3) if(rec[i]!=mx1) mx2=max(mx2, rec[i]);
	return {L.l, R.r, mx1, mx2};
}

然后我们可以求出树上 \(u\to v\) 路径上的 \(LCA\),记为 \(a\)。

那么只需要对路径 \(u\to a\) 以及 \(v\to a\) 分别询问一次得到两条树链的信息(最大值、次大值)再合并这两个信息即可。

全部代码:

// Problem: 次小生成树
// Contest: AcWing
// URL: https://www.acwing.com/problem/content/358/
// Memory Limit: 512 MB
// Time Limit: 1000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#pragma GCC optimize("O3")
#include<iostream>
#include<cstring>
#include<cmath>
#include<algorithm>
#include<vector>
using namespace std;
 
#define debug(x) cerr << #x << ": " << (x) << endl
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define dwn(i,a,b) for(int i=(a);i>=(b);i--)
#define pb push_back
#define all(x) (x).begin(), (x).end()
 
#define x first
#define y second
using pii = pair<int, int>;
using ll = long long;
 
inline void read(int &x){
    int s=0; x=1;
    char ch=getchar();
    while(ch<'0' || ch>'9') {if(ch=='-')x=-1;ch=getchar();}
    while(ch>='0' && ch<='9') s=(s<<3)+(s<<1)+ch-'0',ch=getchar();
    x*=s;
}

const int N=1e5+5, M=1e6+5, INF=1e9+10;

int n, m;

struct Msg{
	int u, v, w, vis;
}buf[M];

struct Edge{
	int to, w, next;
}e[M];

int h[N], tot;

void add(int u, int v, int w){
	e[tot].to=v, e[tot].w=w, e[tot].next=h[u], h[u]=tot++;
}

int f[N];

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

ll MST(){
	ll res=0;
	rep(i,1,n) f[i]=i;
	rep(i,1,m){
		int u=buf[i].u, v=buf[i].v, w=buf[i].w;
		int pu=find(u), pv=find(v);
		if(pu!=pv) f[pu]=pv, res+=w, add(u, v, w), add(v, u, w), buf[i].vis=true;
	}
	return res;
}

int fa[N], son[N], sz[N], dep[N];
int W[N], val[N];
int top[N], dfn[N], ts;

void dfs1(int u, int f, int d){
	fa[u]=f, sz[u]=1, dep[u]=d;
	for(int i=h[u]; ~i; i=e[i].next){
		int go=e[i].to;
		if(go==f) continue;
		W[go]=e[i].w;
		dfs1(go, u, d+1);
		sz[u]+=sz[go];
		if(sz[son[u]]<sz[go]) son[u]=go;
	}
}

void dfs2(int u, int t){
	top[u]=t, dfn[u]=++ts, val[ts]=W[u];
	if(!son[u]) return;
	dfs2(son[u], t);
	for(int i=h[u]; ~i; i=e[i].next){
		int go=e[i].to;
		if(go==fa[u] || go==son[u]) continue;
		dfs2(go, go);
	}
}

struct Node{
	int l, r;
	int mx1, mx2;
	
	#define ls u<<1
	#define rs u<<1|1
}tr[N<<2];

Node merge(Node &L, Node &R){
	int rec[]={L.mx1, L.mx2, R.mx1, R.mx2};
	int mx1=-INF, mx2=-INF;
	for(auto i: rec) mx1=max(mx1, i); 
	rep(i,0,3) if(rec[i]!=mx1) mx2=max(mx2, rec[i]);
	return {L.l, R.r, mx1, mx2};
}

void build(int u, int l, int r){
	tr[u]={l, r};
	if(l==r) return tr[u].mx1=val[l], tr[u].mx2=-INF, void();
	int mid=l+r>>1;
	build(ls, l, mid), build(rs, mid+1, r);
	tr[u]=merge(tr[ls], tr[rs]);
}

void upd(int u, int x, int fir=-INF, int sec=-INF){
	if(tr[u].l==tr[u].r){
		tr[u].mx1=fir, tr[u].mx2=sec;
		return;
	}
	int mid=tr[u].l+tr[u].r>>1;
	if(x<=mid) upd(ls, x, fir, sec);
	else upd(rs, x, fir, sec);
	tr[u]=merge(tr[ls], tr[rs]);
}

Node query(int u, int l, int r){
	if(l<=tr[u].l && tr[u].r<=r) return tr[u];
	int mid=tr[u].l+tr[u].r>>1;
	Node L={-1}, R={-1};
	if(l<=mid) L=query(ls, l, r);
	if(mid<r) R=query(rs, l, r);
	if(L.l==-1) return R;
	if(R.l==-1) return L;
	return merge(L, R);
}

Node query(int u, int v){
	Node res={-1}, tmp;
	while(top[u]!=top[v]){
		int t=top[u];
		if(~res.l) res=merge((tmp=query(1, dfn[t], dfn[u])), res);
		else res=query(1, dfn[t], dfn[u]);
		u=fa[top[u]];
	}
	if(~res.l) res=merge((tmp=query(1, dfn[v], dfn[u])), res);
	else res=query(1, dfn[v], dfn[u]);
	return res;
}

int lca(int u, int v){
	while(top[u]!=top[v]){
		if(dep[top[u]]<dep[top[v]]) swap(u, v);
		u=fa[top[u]];
	}
	return dep[u]<dep[v]? u: v;
}

signed main(){
	memset(h, -1, sizeof h);	
	cin>>n>>m;
	rep(i,1,m){
		int u, v, w; read(u), read(v), read(w);
		buf[i]={u, v, w, 0};
	}
	
	sort(buf+1, buf+1+m, [&](const Msg &a, const Msg &b){
		return a.w<b.w;
	});
	
	ll res=MST();

	dfs1(1, -1, 1), dfs2(1, 1);
	build(1, 1, n);
	
	int del=INF;
	rep(i,1,m) if(!buf[i].vis){
		int u=buf[i].u, v=buf[i].v, w=buf[i].w;
		if(u==v) continue;
		int a=lca(u, v);
		Node L={-1}, R={-1}, res, rec;
		
		rec=query(a, a);
		upd(1, dfn[a]);
		if(a!=u) L=query(u, a);
		if(a!=v) R=query(v, a);
		upd(1, dfn[a], rec.mx1, rec.mx2);
		
		if(L.l==-1) res=R;
		else if(R.l==-1) res=L;
		else res=merge(L, R);
		
		if(w==res.mx1) del=min(del, w-res.mx2);
		else del=min(del, w-res.mx1);
	}
	cout<<res+del<<endl;
	
	return 0;
}

标签:ch,int,mx2,tr,生成,356,mx1,mx,AcWing
来源: https://www.cnblogs.com/Tenshi/p/16463956.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有