• P4180 【模板】严格次小生成树[BJWC2010]


    P4180 【模板】严格次小生成树[BJWC2010]

    倍增(LCA+最小生成树

    施工队挖断学校光缆导致断网1天(大雾)

    考虑直接枚举不在最小生成树上的边。但是边权可能与最小生成树上的边相等,这样删边时权值不改变,就不满足条件了

    所以我们可以先用倍增处理出最小生成树上任意2点之间的最大边权和次大边权

    枚举每条不在最小生成树上的边,接到树上,再删去最大边(与枚举边的边权不等)或次大边(最大边与枚举边的边权相等),做个判断

    判断边(u,v)时 我们只要询问(u,lca)和(v,lca)就可以了

    找了半个多小时才发现数组不够大....

    #include<iostream>
    #include<cstdio>
    #include<cstring>
    #include<algorithm>
    #include<cctype>
    using namespace std;
    template <typename T> inline T max(T &a,T &b) {return a>b ?a:b;}
    template <typename T> inline T min(T &a,T &b) {return a<b ?a:b;}
    template <typename T> inline void read(T &x){
        char c=getchar(); x=0;
        while(!isdigit(c)) c=getchar();
        while(isdigit(c)) x=(x<<3)+(x<<1)+(c^48),c=getchar();
    }
    struct edge{int from,to,dis;}a[300002];
    int n,m,cnt,f[100002],hd[100002],nxt[600002],ed[600002],poi[600002],val[600002]; //邻接表存边
    bool vis[300002];
    inline bool cmp(const edge &A,const edge &B) {return A.dis<B.dis;}
    inline int find(int x) {return f[x]==x ? x:f[x]=find(f[x]);}
    inline void add(int x,int y,int v){
        nxt[ed[x]]=++cnt; hd[x]=hd[x] ? hd[x]:cnt;
        ed[x]=cnt; poi[cnt]=y; val[cnt]=v;
    }
     
    struct LCA{
        int d[100002],fa[100002][18],fir[100002][18],sec[100002][18]; //fir:最大值 sec:次大值
        inline void dfs(int x,int _fa){ //dfs预处理
            d[x]=d[_fa]+1; fa[x][0]=_fa;
            for(int i=1;(1<<i)<=d[x];++i){
                fa[x][i]=fa[fa[x][i-1]][i-1];
                fir[x][i]=max(fir[x][i-1],fir[fa[x][i-1]][i-1]);
                if(fir[x][i]!=fir[fa[x][i-1]][i-1]) sec[x][i]=max(sec[x][i],fir[fa[x][i-1]][i-1]); //次大值
                else if(fir[x][i]!=fir[x][i-1]) sec[x][i]=max(sec[x][i],fir[x][i-1]);
            }
            for(int i=hd[x];i;i=nxt[i])
                if(poi[i]!=_fa){
                    fir[poi[i]][0]=val[i];
                    sec[poi[i]][0]=-1e9-7;
                    dfs(poi[i],x);
                }
        }
        inline int lca(int x,int y){ //最近公共祖先
            if(d[x]<d[y]) swap(x,y);
            for(int i=17;i>=0;--i)
                if(d[x]-(1<<i)>=d[y])
                    x=fa[x][i];
            if(x==y) return x;
            for(int i=17;i>=0;--i)
                if(fa[x][i]!=fa[y][i])
                    x=fa[x][i],y=fa[y][i];
            return fa[x][0];
        }
        inline int query(int x,int y,int k){ //询问(x,y)之间的最大值
            int res=-1e9-7;
            for(int i=17;i>=0;--i)
                if(d[fa[x][i]]>=d[y]){
                    if(fir[x][i]!=k) res=max(res,fir[x][i]);
                    else res=max(res,sec[x][i]);
                    x=fa[x][i];
                }
            return res;
        }
    }mo1;
    int main(){
        read(n); read(m);
        for(int i=1;i<=n;++i) f[i]=i;
        for(int i=1;i<=m;++i) read(a[i].from),read(a[i].to),read(a[i].dis);
        sort(a+1,a+m+1,cmp); int k=0; long long tot=0,ans=1e16;
        for(int i=1;i<=m&&k<n-1;++i){
            int r1=find(a[i].from),r2=find(a[i].to);
            if(r1!=r2){
                tot+=a[i].dis; f[r1]=r2; vis[i]=1; ++k;
                add(a[i].from,a[i].to,a[i].dis);
                add(a[i].to,a[i].from,a[i].dis);
            }
        }mo1.dfs(1,0);
        for(int i=1;i<=m;++i){
            if(vis[i]) continue; //在最小生成树上
            int _lca=mo1.lca(a[i].from,a[i].to);
            int q1=mo1.query(a[i].from,_lca,a[i].dis); //询问(u,lca)
            int q2=mo1.query(a[i].to,_lca,a[i].dis); //询问(v,lca)
            ans=min(ans,tot-max(q1,q2)+(long long)a[i].dis); //换边找最小值
        }printf("%lld",ans); 
        return 0;
    }
  • 相关阅读:
    【C#】往异步下载的方法传递自定义完成事件
    【WPF】CommandParameter解决多传参问题
    【WPF/C#】使用BackgroundWorker实现多线程/异步操作
    【WPF】弹窗定位、弹窗关闭后再打开的报错
    【WPF/C#】测试下载文件(图片)
    【Unity】初始化物体的旋转角度
    【Unity/C#】DateTime时间字符串,月份用英文显示
    【转】【Unity】DateTime各种时间字符串
    【Unity】UGUI无法修改字体大小
    intellij idea运行Android程序时报错;Unable to locate adb within SDK
  • 原文地址:https://www.cnblogs.com/kafuuchino/p/9682541.html
Copyright © 2020-2023  润新知