[BJWC2010]严格次小生成树
作者:互联网
做题时间:2021.8.8
\(【题目描述】\)
给你一张 \(N(1\leq N\leq 10^5)\) 个点 \(M(1\leq M\leq 3\times 10^5)\) 条边的无向图,求出这张图中严格次小生成树。
\(【输入格式】\)
第一行两个整数 \(N,M\)
接下来 \(M\) 行每行三个整数 \(u,v,w\) 表示图上一条边
\(【输出格式】\)
一行一个数表示答案
\(【考点】\)
最小生成树、倍增DP
\(【做法】\)
次小生成树与最小生成树有关,可以考虑先求出最小生成树,然后遍历不在最小生成树上的每一条边,加入树中,并删除所形成的环中的一条边使其重新构成一棵新树,删除的这条边必定是环中最大或次大的(当最大值=加入的边的边权时,应当删除次大边),因此问题转化为:求最小生成树+枚举边 \((u,v)\) + 倍增求 \((u,lca)\) 和 \((v,lca)\) 路径上的最大值和次大值。
最后一步可以像求LCA一样分成三个部分:
-
求出 \(m_{u,i}\) 与 \(s_{u,i}\) 分别表示 \(u\) 到其 \(2^i\) 级祖先的路径上的最大值以及次大值。具体而言,定义 \(k\) 表示 \(u\) 的 \(2^{i-1}\) 级祖先,若 \(m_{u,i-1}>m_{k,i-1}\) ,则有 \(m_{u,i}=\max(m_{k,i-1},s_{u,i-1})\) ,若 \(m_{u,i-1}<m_{k,i-1}\) ,则有 \(m_{u,i}=\max(m_{u,i-1},s_{k,i-1})\) ,若 \(m_{u,i-1}=m_{k,i-1}\) ,则有 \(m_{u,i}=\max(s_{u,i-1},s_{k,i-1})\)
-
将 \(u,v\) 中深度较大的先上跳至与 \(v\) 深度相同处(设它是 \(u\) ),上跳过程中求最大值与次大值与1同理。
-
将 \(u,v\) 一起上跳至lca的儿子处。
注意:输入数据中可能会有自环,影响答案,要进行判断;
#include<cstdio>
#include<iomanip>
#include<algorithm>
using namespace std;
const int N=1e5+50,M=3e5+50;
typedef long long ll;
struct edge{
int to,nxt,val;
}a[N<<1];
int head[N],tot;
struct G{
int u,v,w;
}g[M];
int fa[N],f[N][30],dep[N];
int maxn[N][30],Sec_maxn[N][30];
bool vis[M];
int n,m;
inline ll Min(ll a,ll b){return a<b?a:b;}
inline ll Max(ll a,ll b){return a>b?a:b;}
inline ll Swap(int &x,int &y){int t=x;x=y;y=t;}
#define m1 maxn[x][i-1]
#define m2 maxn[f[x][i-1]][i-1]
#define m3 maxn[x][i]
#define s1 Sec_maxn[x][i-1]
#define s2 Sec_maxn[f[x][i-1]][i-1]
#define s3 Sec_maxn[x][i]
void Work1(int x,int fa,int pre)
{
dep[x]=dep[fa]+1,f[x][0]=fa;
maxn[x][0]=pre;
for(int i=1;i<=20;i++){
f[x][i]=f[f[x][i-1]][i-1];
m3=Max(m1,m2);
//分三种情况
if(m1>m2) s3=Max(m2,s1);//m1>m2>s2 ,只有m2和s1有机会成为次大值
if(m1==m2) s3=Max(s1,s2);//m1==m2>s1,s2 ,只有m2和s1有机会成为次大值
if(m1<m2) s3=Max(m1,s2);//m2>m1>s1 ,只有m1和s2有机会成为次大值
}
}
void Work2(int x,int i,int &Maxn,int &Sec)
{
//分三种情况,同上
if(Maxn>m3) Sec=Max(m3,Sec);
if(Maxn==m3) Sec=Max(s3,Sec);
if(Maxn<m3) Sec=Max(Maxn,s3);
Maxn=Max(m3,Maxn);
}
ll Find(int x,int y,int z)//上跳求最大值和次大值
{
int Maxn=0,Sec=0;
if(dep[x]<dep[y]) Swap(x,y);
for(int i=20;i>=0;i--){
if(dep[f[x][i]]>=dep[y]){
Work2(x,i,Maxn,Sec);//求出x到x的2^i级祖先路径上的最大值和次大值
x=f[x][i];
}
}
if(x==y){
if(Maxn==z) return Sec;
return Maxn;
}
for(int i=20;i>=0;i--){
if(f[x][i]!=f[y][i]){
Work2(x,i,Maxn,Sec);//求出x到x的2^i级祖先路径上的最大值和次大值
Work2(y,i,Maxn,Sec);//求出y到y的2^i级祖先路径上的最大值和次大值
x=f[x][i],y=f[y][i];
}
}
Work2(x,0,Maxn,Sec);//最后x到lca和y到lca
Work2(y,0,Maxn,Sec);
if(Maxn==z) return Sec;
return Maxn;
}
void DFS(int u,int fa,int pre)
{
Work1(u,fa,pre);
for(int i=head[u];i;i=a[i].nxt){
int v=a[i].to;
if(v!=fa) DFS(v,u,a[i].val);
}
}
namespace MST{//求最小生成树
bool cmp(G a,G b){return a.w<b.w;}
void add(int u,int v,int w)
{
tot++;
a[tot].to=v;
a[tot].val=w;
a[tot].nxt=head[u];
head[u]=tot;
}
int Find(int x)
{
if(x==fa[x]) return x;
return fa[x]=Find(fa[x]);
}
bool Merge(int x,int y)
{
int X=Find(x),Y=Find(y);
if(X==Y) return false;
fa[X]=Y;return true;
}
ll Kruskal()
{
ll ans=0;
int cnt=0;
sort(g+1,g+1+m,cmp);
for(int i=1;i<=m;i++){
if(Merge(g[i].u,g[i].v)){
cnt++;
add(g[i].u,g[i].v,g[i].w);
add(g[i].v,g[i].u,g[i].w);
vis[i]=true;
ans+=(ll)g[i].w;
}
if(cnt==n-1) break;
}
return ans;
}
}
using namespace MST;
inline int Read()
{
int x=0;
char ch=getchar();
while(ch>'9'||ch<'0') ch=getchar();
while(ch>='0'&&ch<='9'){
x=(x<<1)+(x<<3)+ch-48;
ch=getchar();
}
return x;
}
int main()
{
n=Read(),m=Read();
for(int i=1;i<=n;i++) fa[i]=i;//并查集初始化
int root=0;
for(int i=1;i<=m;i++){
g[i].u=Read(),g[i].v=Read(),g[i].w=Read();
root=g[i].u;
}
ll ans=Kruskal(),tmp=2147483645;
DFS(root,0,0);
for(int i=1;i<=m;i++){
if(!vis[i]&&g[i].u!=g[i].v){//若这条边不在MST中且不是自环
tmp=Min(tmp,g[i].w-Find(g[i].u,g[i].v,g[i].w));
}
}
printf("%lld\n",ans+tmp);
return 0;
}
标签:maxn,int,s1,生成,严格,Sec,m2,Maxn,BJWC2010 来源: https://www.cnblogs.com/Unlimited-Chan/p/16471846.html