(luogu4180) [Beijing2010组队]次小生成树Tree

严格次小生成树

首先看看如果不严格我们怎么办。

非严格次小生成树怎么做

由此,我们发现一个结论,求非严格次小生成树,只需要先用kruskal算法求得最小生成树,然后暴力枚举非树边,替换路径最大边即可。

那要是严格呢?

我们发现如果是严格的次小生成树,那么将一条边替换另一条时,这两条边的权值一定不相同

但是,我们知道,替换边肯定大于等于被替换边(因为如果替换边小于被替换边,就存在一颗包含替换边而不包含被替换边的一棵权值更小的生成树,原树就不是最小生成树了)

所以替换边要么等于路径上最大的边,要么比最大的边还大。

利用这个性质,我们只需要维护路径中的最大值和次大值,当替换边等于路径上的最大值,我们直接换用严格次大值即可。

一些细节

1.我维护两点之间路径最大值用的是LCT,但是正解是LCA。LCT必须要开O2才能跑过去。

2.数组要开足够大,最后统计答案时要开long long,不然会爆int

我的代码

#include <algorithm>
#include <iostream>
#include <cstring>
#include <cstdio>
#include <vector>
#include <queue>
#define rg register int
#define ll long long
#define RG register
#define il inline
using namespace std;

il ll gi() {
    RG ll x=0;rg o=0;RG char ch=getchar();
    while(ch!='-'&&(ch<'0'||'9'<ch)) ch=getchar();
    if(ch=='-') o=1,ch=getchar();
    while('0'<=ch&&ch<='9') x=(x<<1)+(x<<3)+ch-'0',ch=getchar();
    return o?-x:x;
}
int n,m;
#define SZ 7000010

struct Edge {int a,b;ll w;}e[SZ];
bool cmp(Edge a,Edge b) {return a.w<b.w;}

#define lson tr[x].ch[0]
#define rson tr[x].ch[1]
struct Splaytree{int fa,ch[2],rev,mxp,mxp2;}tr[SZ];
il void pushup(rg x)
{
    tr[x].mxp=x; tr[x].mxp2=0;
    if(e[tr[lson].mxp].w>e[tr[x].mxp].w) tr[x].mxp=tr[lson].mxp;
    if(e[tr[rson].mxp].w>e[tr[x].mxp].w) tr[x].mxp=tr[rson].mxp;
    // 维护 最大
    if(e[tr[lson].mxp].w!=e[tr[x].mxp].w && (e[tr[lson].mxp].w>e[tr[x].mxp].w||!tr[x].mxp2) ) tr[x].mxp2=tr[lson].mxp;
    if(e[tr[lson].mxp2].w!=e[tr[x].mxp].w && (e[tr[lson].mxp2].w>e[tr[x].mxp].w||!tr[x].mxp2) ) tr[x].mxp2=tr[lson].mxp2;    
    if(e[tr[rson].mxp].w!=e[tr[x].mxp].w && (e[tr[rson].mxp].w>e[tr[x].mxp].w||!tr[x].mxp2) ) tr[x].mxp2=tr[rson].mxp;
    if(e[tr[rson].mxp2].w!=e[tr[x].mxp].w && (e[tr[rson].mxp2].w>e[tr[x].mxp].w||!tr[x].mxp2) ) tr[x].mxp2=tr[rson].mxp2;        
    // 维护严格次大
}
il void pushdown(rg x)
{
    if(tr[x].rev)
    {
        tr[lson].rev^=1,tr[rson].rev^=1;
        swap(lson,rson),tr[x].rev=0;
    }
}
il bool isroot(rg x)
{
    return tr[tr[x].fa].ch[0]!=x && tr[tr[x].fa].ch[1]!=x;
}
il void rotate(rg x)
{
    rg y=tr[x].fa,z=tr[y].fa;
    rg k=tr[y].ch[1]==x;
    if(!isroot(y)) tr[z].ch[y==tr[z].ch[1]]=x;tr[x].fa=z;
    tr[y].ch[k]=tr[x].ch[k^1],tr[tr[x].ch[k^1]].fa=y;
    tr[x].ch[k^1]=y,tr[y].fa=x;
    pushup(y),pushup(x);
}
int stk[SZ],top; 
il void splay(rg x)
{
    stk[top=1]=x;
    for(rg i=x;!isroot(i);i=tr[i].fa) stk[++top]=tr[i].fa;
    for(;top;--top) pushdown(stk[top]);
    while(!isroot(x))
    {
        rg y=tr[x].fa,z=tr[y].fa;
        if(!isroot(y))
         (tr[y].ch[0]==x)^(tr[z].ch[0]==y)?rotate(x):rotate(y);
        rotate(x);
    }
}
il void access(rg x) {for(rg y=0;x;y=x,x=tr[x].fa)splay(x),rson=y,pushup(x);}
il void makeroot(rg x) {access(x);splay(x);tr[x].rev^=1;}
il int findroot(rg x) {access(x);splay(x);while(lson) x=lson;return x;}
il void split(rg x,rg y) {makeroot(x);access(y);splay(y);}
il int query(rg x,rg y) {split(x,y);return tr[y].mxp;} //求x 到 y最大值 
il int query2(rg x,rg y) {split(x,y);return tr[y].mxp2;} // 求x 到 y严格次大值
il void link(rg x,rg y) {makeroot(x);tr[x].fa=y;}
il void cut(rg x,rg y) {split(x,y);if(tr[y].ch[0]==x)tr[y].ch[0]=tr[x].fa=0;}
int fa[SZ];int find_fa(rg x) {if(x!=fa[x]) fa[x]=find_fa(fa[x]);return fa[x];} //一行并查集
bool check[SZ];

int main()
{

    n=gi(),m=gi();
    for(rg i=1;i<=m;++i) e[i]=(Edge){gi(),gi(),gi()};
    // 先求一遍最小生成树 ans 记录最小生成树边的大小
    RG ll ans=0;
    sort(e+1,e+1+m,cmp);    
    for(rg i=1;i<=n;++i) fa[i]=i; // 初始化并查集
    for(rg f1,f2,i=1;i<=m;++i)
    {
        f1=find_fa(e[i].a);
        f2=find_fa(e[i].b);
        if(f1!=f2)
        {
            check[i]=1; // check=1 表示最小生成树中有这一条边 反之
            fa[f1]=f2;
            ans+=e[i].w;
            link(e[i].a+m,i);
            link(e[i].b+m,i);
        }
    }

#define INF 2147483647
#define Getmin(a,b) (a)=(a)>(b)?(b):(a) 

    RG ll Ans=INF;
    for(rg f1,f2,i=1;i<=m;++i)
    {
        if(check[i]) continue; //我们选择不再最小生成树上的边
        rg mxp=query(e[i].a+m,e[i].b+m);
        if(e[mxp].w==e[i].w)
        {
            rg mxp2=query2(e[i].a+m,e[i].b+m);
            if(!mxp2 || e[mxp2].w==e[mxp].w) continue;
            Getmin(Ans,e[i].w-e[mxp2].w);
        }
        else Getmin(Ans,e[i].w-e[mxp].w);
    }

    cout<<ans+Ans;
    return 0;
}

posted @ 2018-02-06 19:58  TPLY  阅读(...)  评论(...编辑  收藏
free web counter
free web counter