次小生成树(严格次小)(模版)
作者:互联网
这道题调了快2h,因为一个极其愚蠢的错误
算法要素:kruskal+倍增LCA+同步求最大值次大值
算法分析:
基本思路:
首先肯定要生成一个最小生成树,然后有两种枚举方式:
<1>可以选择枚举生成树上每一条边
<2>枚举每一条非树边
为什么要枚举呢?显然是因为这个问题并没有明显的单调性,无法通过贪心或者二分来排除一些无用的情况。
那么我们还要面临最后一个问题:次小生成树分为严格次小生成树和非严格次小生成树。这个问题稍后再讨论。
具体实现
先考虑如何枚举树上的每一条边:
(1)最短路做法:删去一条树枝边,用djst或者spfa求出两点之间不经过原树枝边的最短路径。若要求严格次小,求严格次短路。若不要求严格次小,则直接求最短路即可。
tips:复杂度O(nmlogm),不够优秀,只能过部分分。
(2)我想不出其他的了qwq
考虑如何枚举非树边来代替树边:
考虑将一条非树边加入生成树中,这一过程肯定导致了有且仅有一条原树枝边可以被从生成树中删去。
为了使生成树次小,那么要删去的一定是当前可删去的边中最大的一条。
经过画图就可以发现,可以删去的边在非树边两端点和二者LCA所形成的环上。(图片稍后补充)
因此只需找到这个环上的最大边,统计答案即可。
由于倍增LCA使用了st表的预处理方法,而此方法也可用于统计最大值和次大值,故可以同步进行。
最后理顺一下算法思路:
step1:求出kruskal,建立最小生成树
step2:倍增LCA,同时预处理最大值和次大值(若不要求严格次小,则只预处理最大值即可)
step3:枚举每条非树边,求出环上的最大值,统计答案,求出最小值。
最终复杂度O(2mlongm+n+18n+m)----》O(mlogm+19n+m)十分优秀,过1e5的数据点基本没什么问题
tips:需要注意的问题我调题时犯的愚蠢错误
(1)要给次小值赋一个极小值的初值
(2)最后枚举的是非树边而非树边
(3)建最小生成树的时候需要加双向边,因为需要在树上求LCA,不加双向边会挂到20分
说实话就算是根据树的定义也应该加双向边呀啊喂,我为什么会被这个问题卡了快两个小时a。。
那么就来几道模板题吧:
-洛谷P4180 [BJWC2010]严格次小生成树
loj#10068. 「一本通 3.1 练习 3」秘密的牛奶运输
loj#10133. 「一本通 4.4 例 4」次小生成树
唯一不足之处就是我找到的这三道题完全一样。。。。只是题面背景不一样而已。。
Code
#include<bits/stdc++.h>
using namespace std;
#define ll long long
const int maxn=1e5+50;
const ll INF=2e18;
ll n,m,ecnt=-1,tot;
ll fa[maxn],head[maxn];
ll dep[maxn],anc[maxn][25];
ll ancmax[maxn][25],ancmin[maxn][25];
ll totans=INF,sum=0;
bool vis[maxn];
struct lena
{
ll u,v,w;
}edge[maxn<<3];
struct mint
{
ll nxt,u,v,w;
}e[maxn<<1];
bool cmp(lena a,lena b)
{
return a.w<b.w;
}
inline void addline(ll u,ll v,ll w)
{
e[++ecnt].nxt=head[u];
e[ecnt].v=v;
e[ecnt].u=u;
e[ecnt].w=w;
head[u]=ecnt;
}
ll Find(ll x)
{
if(x==fa[x]) return x;
return fa[x]=Find(fa[x]);
}
void dfs(ll x,ll fa)
{
dep[x]=dep[fa]+1;
anc[x][0]=fa;
for(ll i=head[x];~i;i=e[i].nxt)
{
int v=e[i].v;
if(v==fa) continue;
ancmax[v][0]=e[i].w;
ancmin[v][0]=-INF;
dfs(v,x);
}
}
void cal()
{
for(ll i=1;i<=18;++i)
{
for(ll x=1;x<=n;++x)
{
anc[x][i]=anc[anc[x][i-1]][i-1];
ancmax[x][i]=max(ancmax[x][i-1],ancmax[anc[x][i-1]][i-1]);
ancmin[x][i]=max(ancmin[x][i-1],ancmin[anc[x][i-1]][i-1]);
if(ancmax[x][i-1]>ancmax[anc[x][i-1]][i-1]) ancmin[x][i]=max(ancmin[x][i],ancmax[anc[x][i-1]][i-1]);
else if(ancmax[x][i-1]<ancmax[anc[x][i-1]][i-1]) ancmin[x][i]=max(ancmin[x][i],ancmax[x][i-1]);
}
}
}
ll LCA(ll x,ll y)
{
if(dep[x]<dep[y]) swap(x,y);
for(ll i=18;i>=0;--i)
{
if(dep[anc[x][i]]>=dep[y]) x=anc[x][i];
}
if(x==y) return x;
for(ll i=18;i>=0;--i)
{
if(anc[x][i]!=anc[y][i])
{
x=anc[x][i];
y=anc[y][i];
}
}
return anc[x][0];
}
ll getmax(ll x,ll fath,ll maxx)
{
ll ans=-INF;
for(ll i=18;i>=0;--i)
{
if(dep[anc[x][i]]>=dep[fath])
{
if(ancmax[x][i]!=maxx) ans=max(ans,ancmax[x][i]);
else ans=max(ans,ancmin[x][i]);
x=anc[x][i];
}
}
return ans;
}
int main()
{
memset(head,-1,sizeof(head));
scanf("%lld%lld",&n,&m);
for(ll i=1;i<=m;++i)
{
ll x,y,z;
scanf("%lld%lld%lld",&x,&y,&z);
edge[i].u=x;
edge[i].v=y;
edge[i].w=z;
}
for(ll i=1;i<=n;++i) fa[i]=i;
sort(edge+1,edge+m+1,cmp);
for(ll i=1;i<=m;++i)
{
ll x=Find(edge[i].u),y=Find(edge[i].v);
if(x!=y)
{
fa[x]=y;
addline(edge[i].u,edge[i].v,edge[i].w);
addline(edge[i].v,edge[i].u,edge[i].w);
sum+=edge[i].w;
tot++;
vis[i]=true;
if(tot==n-1) break;
}
}
ancmin[1][0]=-INF;
dfs(1,0);
cal();
totans=INF;
for(ll i=1;i<=m;++i)
{
if(!vis[i])
{
ll x=edge[i].u,y=edge[i].v;
ll fath=LCA(x,y);
ll maxx=max(getmax(x,fath,edge[i].w),getmax(y,fath,edge[i].w));
totans=min(totans,sum-maxx+edge[i].w);
}
}
printf("%lld",totans);
return 0;
}
标签:anc,模版,ll,生成,严格,枚举,maxn,ans 来源: https://www.cnblogs.com/mint-hexagram/p/15464102.html