洛谷 P4180 [BJWC2010]严格次小生成树
JDOJ 1059: 次小生成树
Description
小C最近学了很多最小生成树的算法,Prim算法、Kurskal算法、消圈算法等等。正当小C洋洋得意之时,小P又来泼小C冷水了。小P说,让小C求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小的,也就是说:如果最小生成树选择的边集是Em,严格次小生成树选择的边集是Es,那么需要满足:(value(e) 表示边e的权值) Sigma(value(e))(其中e∈Em)小于Sigma(value(e))(其中e∈Es) 这下小C蒙了,他找到了你,希望你帮他解决这个问题。
Input
输入文件第一行包含两个整数N和M,表示无向图的点数与边数。接下来M行,每行3个数x y z表示,点x和点y之间有一条边,边的权值为z。
Output
输出文件包含一行,仅一个数,表示严格次小生成树的边权和。(数据保证必定存在严格次小生成树)
Sample Input
5 6 1 2 1 1 3 2 2 4 3 3 5 4 3 4 3 4 5 6
Sample Output
11
HINT
数据中无向图无自环; 50% 的数据N≤2000 M≤3000; 80% 的数据N≤50000 M≤100000; 100% 的数据N≤100000 M≤300000 ,边权值非负且不超过10^9。
题解:
又是一道看起来简单但是并不是很简单的题。(姹紫嫣红)
首先我们很容易通过直觉判断,次小生成树一定是通过MST最小生成树经过并不是很大的改动得到的。
于是我们先把问题简化,这题求严格次小,那么非严格次小怎么求呢?
根据Kruskal算法的本质:贪心。对于一条链接((u,v))但没有被选入MST的边来讲,它一定比路径((u,v))中的最长边短(也有可能等于)。那么我们只需要每次遍历没有选的边,用其替换((u,v))之间的最长边即可。
非严格的求完了。
严格的怎么求呢?
与上次的思路一样,多存一个((u,v))路径上的次大值,如果最大值和边权相等,就替换次大边。
用倍增LCA即可求解。
常数稍大,卡卡常可过。
代码:
#include<cstdio>
#include<algorithm>
#define R register
#define ll long long
using namespace std;
const int maxm=3*1e5+10;
const int maxn=1e5+10;
const ll INF=99824435300000;
int n,m,cnt;
int fa[maxn];
bool v[maxm];
ll dist;
int to[maxn<<1],nxt[maxn<<1],head[maxn],val[maxn<<1],tot;
int f[maxn][21],deep[maxn];
ll maxx[maxn][21],minn[maxn][21];
char *p1,*p2,buf[100000];
#define nc() (p1==p2 && (p2=(p1=buf)+fread(buf,1,100000,stdin),p1==p2)?EOF:*p1++)
inline int read()
{
int x=0,f=1;
char ch=nc();
while(ch<48||ch>57)
{
if(ch=='-')
f=-1;
ch=nc();
}
while(ch>=48&&ch<=57)
x=x*10+ch-48,ch=nc();
return x*f;
}
struct edge
{
int x,y,z;
}e[maxm];
inline bool cmp(edge a,edge b)
{
return a.z<b.z;
}
inline int find(int x)
{
return x==fa[x]?x:fa[x]=find(fa[x]);
}
inline void add(int x,int y,int z)
{
to[++tot]=y;
nxt[tot]=head[x];
val[tot]=z;
head[x]=tot;
}
inline void dfs(int x,int from)
{
deep[x]=deep[from]+1;
f[x][0]=from;
minn[x][0]=-INF;
for(R int i=head[x];i;i=nxt[i])
{
int y=to[i];
if(y==from)
continue;
maxx[y][0]=val[i];
dfs(y,x);
}
}
inline int Lca(int x,int y)
{
if(deep[x]<deep[y])
swap(x,y);
for(R int i=18;i>=0;i--)
if(deep[f[x][i]]>=deep[y])
x=f[x][i];
if(x==y)
return x;
for(R int i=18;i>=0;i--)
if(f[x][i]^f[y][i])
x=f[x][i],y=f[y][i];
return f[x][0];
}
inline ll qmax(int x,int y,ll mx)
{
ll ret=-INF;
for(R int i=18;i>=0;i--)
if(deep[f[x][i]]>=deep[y])
{
if(mx!=maxx[x][i])
ret=max(ret,maxx[x][i]);
else
ret=max(ret,minn[x][i]);
x=f[x][i];
}
return ret;
}
int main()
{
n=read();m=read();
for(R int i=1;i<=m;i++)
e[i].x=read(),e[i].y=read(),e[i].z=read();
sort(e+1,e+m+1,cmp);
for(R int i=1;i<=n;i++)
fa[i]=i;
for(R int i=1;i<=m;i++)
{
int fx=find(e[i].x);
int fy=find(e[i].y);
if(fx!=fy)
{
dist+=e[i].z;
fa[fx]=fy;
add(e[i].x,e[i].y,e[i].z);
add(e[i].y,e[i].x,e[i].z);
v[i]=1;
cnt++;
}
if(cnt==n-1)
break;
}
dfs(1,0);
for(R int i=1;i<=18;i++)
for(R int j=1;j<=n;j++)
{
f[j][i]=f[f[j][i-1]][i-1];
maxx[j][i]=max(maxx[j][i-1],maxx[f[j][i-1]][i-1]);
minn[j][i]=max(minn[j][i-1],minn[f[j][i-1]][i-1]);
if(maxx[j][i-1]>maxx[f[j][i-1]][i-1])
minn[j][i]=max(minn[j][i],maxx[f[j][i-1]][i-1]);
else if(maxx[j][i-1]<maxx[f[j][i-1]][i-1])
minn[j][i]=max(minn[j][i],maxx[j][i-1]);
}
ll ans=INF;
for(R int i=1;i<=m;i++)
if(!v[i])
{
int u=e[i].x;
int v=e[i].y;
ll d=e[i].z;
int lca=Lca(u,v);
ll maxu=qmax(u,lca,d);
ll maxv=qmax(v,lca,d);
ans=min(ans,dist-max(maxu,maxv)+d);
}
printf("%lld",ans);
return 0;
}