BZOJ1977: [BeiJing2010组队]次小生成树 Tree

题目描述:戳这里
题解:
求非严格次小生成树是比较简单的,但是严格的就有点(超级)麻烦了。
我们可以考虑一下用克鲁斯卡尔做最小生成树的过程,一定是找了一些最小的边来构造。
我们假设造好了最小生成树,那么任意一条没有被选择过的边加入树都会在树上形成一个环。
则我们只需要考虑取出当前环上权值最大的一条边,换成当前这条,看看是不是最小次大的就可以了。
但是题目中说了是严格次小生成树,所以有可能你当前的这条边和环上最大边权值相等,这样就比较尴尬了,但是我们如果不用这条边来修正答案有可能得不到最优解。
那么就可以考虑一下维护某一条链上的最大值和次大值(严格,意思就是次大值必须小于最大值),这样如果不能和最大值交换就和次大值交换,就可以维护这个严格次小值了。
但是怎样维护一条链上的最大值和次大值呢,我们可以考虑倍增维护,对于每一条非树边,只要对于它的两个顶点求一个lca,再直接倍增出最大值和次大值,修正一下答案就行了。

代码奇长无比,珂怕

#include<cstdio>
#include<string>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
const int maxn=100005,maxm=300005;
int n,m,tot,lnk[maxn],son[2*maxn],nxt[2*maxn],w[2*maxn],fa[maxn],dep[maxn];
int f[maxn][21],maxx[maxn][21],maxx1[maxn][21];
ll sum,ans;
struct dyt{
    int x,y,w;
    bool pd;
    bool operator <(const dyt &b) const{return w<b.w;}
}a[maxm];
void add(int x,int y,int z){
    son[++tot]=y,w[tot]=z,nxt[tot]=lnk[x],lnk[x]=tot;
}
int getfa(int x){if (fa[x]!=x) fa[x]=getfa(fa[x]); return fa[x];}
void dfs(int x,int las){
    for (int j=lnk[x];j;j=nxt[j])
    if (las!=son[j]) {
        maxx[son[j]][0]=w[j]; f[son[j]][0]=x; dep[son[j]]=dep[x]+1;
        dfs(son[j],x);
    }
}
void make_(){
    for (int j=1;j<=20;j++)
    for (int i=1;i<=n;i++) {
        f[i][j]=f[f[i][j-1]][j-1];
        maxx[i][j]=max(maxx[i][j-1],maxx[f[i][j-1]][j-1]);
        if (maxx[i][j-1]<maxx[i][j]) maxx1[i][j]=maxx[i][j-1];
        if (maxx1[i][j-1]<maxx[i][j]&&maxx1[i][j-1]>maxx1[i][j]) maxx1[i][j]=maxx1[i][j-1];     
        if (maxx[f[i][j-1]][j-1]<maxx[i][j]&&maxx[f[i][j-1]][j-1]>maxx1[i][j]) maxx1[i][j]=maxx[f[i][j-1]][j-1];
        if (maxx1[f[i][j-1]][j-1]<maxx[i][j]&&maxx1[f[i][j-1]][j-1]>maxx1[i][j]) maxx1[i][j]=maxx1[f[i][j-1]][j-1];
    }
}
int get(int x,int y){
    if (dep[x]<dep[y]) swap(x,y);
    for (int j=20;j>=0;j--) if (dep[f[x][j]]>dep[y]) x=f[x][j];
    if (dep[x]>dep[y]) x=f[x][0];
    if (x==y) return y;
    for (int j=20;j>=0;j--) if (f[x][j]!=f[y][j]) x=f[x][j],y=f[y][j];
    return f[x][0];
}
void doit(int x,int y,int g,int num){
    int mx=0,mx1=0;
    for (int j=20;j>=0;j--)
    if (dep[f[x][j]]>=dep[g]) {
        if (maxx[x][j]>mx){
            mx1=mx,mx=maxx[x][j];
            if (maxx1[x][j]>mx1) mx1=maxx1[x][j];
        } else if (maxx[x][j]>mx1&&maxx[x][j]!=mx) mx1=maxx[x][j];
        x=f[x][j];
    }
    for (int j=20;j>=0;j--)
    if (dep[f[y][j]]>=dep[g]) {
        if (maxx[y][j]>mx){
            mx1=mx,mx=maxx[y][j];
            if (maxx1[y][j]>mx1) mx1=maxx1[y][j];
        } else if (maxx[y][j]>mx1&&maxx[y][j]!=mx) mx1=maxx[y][j];
        y=f[y][j];
    }
    if (mx!=num) ans=min(ans,sum+num-mx); else ans=min(ans,sum+num-mx1);
}
int main(){
    scanf("%d %d",&n,&m);
    for (int i=1;i<=n;i++) fa[i]=i;
    for (int i=1;i<=m;i++) scanf("%d %d %d",&a[i].x,&a[i].y,&a[i].w);
    sort(a+1,a+1+m);
    for (int i=1;i<=m;i++) {
        int fax=getfa(a[i].x),fay=getfa(a[i].y);
        if (fax!=fay) {
            fa[fax]=fay; a[i].pd=1; sum+=a[i].w;
            add(a[i].x,a[i].y,a[i].w);
            add(a[i].y,a[i].x,a[i].w);
        }
    }
    dfs(1,0); make_(); ans=1e17;
    for (int i=1;i<=m;i++)
    if (!a[i].pd) {
        int grand=get(a[i].x,a[i].y);
        doit(a[i].x,a[i].y,grand,a[i].w);
    }
    printf("%lld\n",ans);
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值