洛谷 P4180 [BJWC2010]严格次小生成树

洛谷 P4180 [BJWC2010]严格次小生成树

洛谷传送门

JDOJ 1059: 次小生成树

JDOJ传送门

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;
}
posted @ 2020-09-24 15:49  Seaway-Fu  阅读(163)  评论(0编辑  收藏  举报