zoukankan      html  css  js  c++  java
  • 【次小生成树】bzoj1977 [BeiJing2010组队]次小生成树 Tree

    Description

    小 C 最近学了很多最小生成树的算法,Prim 算法、Kurskal 算法、消圈算法等等。 正当小 C 洋洋得意之时,小 P 又来泼小 C 冷水了。小 P 说,让小 C 求出一个无向图的次小生成树,而且这个次小生成树还得是严格次小的,也就是说: 如果最小生成树选择的边集是 EM,严格次小生成树选择的边集是 ES,那么需要满足:(value(e) 表示边 e的权值) 这下小 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≤2 000 M≤3 000; 80% 的数据N≤50 000 M≤100 000; 100% 的数据N≤100 000 M≤300 000 ,边权值非负且不超过 10^9 。

    题解

    就是求严格次小生成树

    目前常见做法是先用Kurskal求出最小生成树

    然后枚举不在树上的边,试着把它连到树上,找到形成的环上边权最大但不等于新加的边权的一条边,计算出(最小生成树权值+新加边边权-找到的最大)

    对每条不在最小生成树上的边计算过之后再取min就是结果

    而要求形成的环上边权最大但不等于新加的边权的一条边,我们就可以用树剖维护一个最大值和次大值,然后就可以轻松(mlog2n)得出答案了

    我才不会说我边权转化成点权的时候没按dfn序结果调了一整天也没发现呢,哼

    代码

    //by 减维
    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<queue>
    #include<cstdlib>
    #include<ctime>
    #include<cmath>
    #include<algorithm>
    #define ll long long
    #define ls l,mid,v<<1
    #define rs mid+1,r,v<<1|1
    #define getm mid=(l+r)>>1
    using namespace std;
    
    struct us{
        int x,y;
        ll v;
    }edg[300005];
    
    struct edge{
        int to,ne;
        ll v;
    }e[200005];
    
    int n,m,num,ecnt,fa[100005],dep[100005],siz[100005],son[100005],val[100005];
    int f[100005],dfn[100005],out[100005],head[100005],top[100005];
    long long ans1,ans2,ans,ma[400005],ma2[400005];
    bool pd[300005];
    ll inf=1ll<<62;
    
    bool cmp(const us&x,const us&y){return x.v<y.v;}
    bool cm2(int x,int y){return x>y;}
    
    int find(int x)
    {
        if(x==fa[x])return x;
        fa[x]=find(fa[x]);
        return fa[x];
    }
    
    void add(int x,int y,int z)
    {
        e[++ecnt].to=y;
        e[ecnt].ne=head[x];
        e[ecnt].v=z;
        head[x]=ecnt;
    }
    
    void df1(int x)
    {
        dep[x]=dep[f[x]]+1;
        siz[x]=1;
        for(int i=head[x];i;i=e[i].ne)
        {
            int dd=e[i].to;
            if(dd==f[x])continue;
            f[dd]=x;
            df1(dd);
            siz[x]+=siz[dd];
            if(!son[x]||siz[son[x]]<siz[dd])
                son[x]=dd;
        }
    }
    
    void dfs(int x,int tp)
    {
        top[x]=tp;
        dfn[x]=++num;
        if(son[x])dfs(son[x],tp);
        for(int i=head[x];i;i=e[i].ne)
        {
            int dd=e[i].to;
            if(dd==f[x])continue;
            if(dd==son[x]){
                val[dfn[dd]]=e[i].v;
                continue;
            }
            dfs(dd,dd);
            val[dfn[dd]]=e[i].v;
        }
        out[x]=num;
    }
    
    void upda(int v)
    {
        int x[5];
        x[1]=ma[v<<1],x[2]=ma[v<<1|1],x[3]=ma2[v<<1],x[4]=ma2[v<<1|1];
        sort(x+1,x+5,cm2);
        ma[v]=x[1];
        ma2[v]=x[2];
    }
    
    void print(int l,int r,int v)
    {
        if(l==r){
            printf("%d %d
    ",ma[v],ma2[v]);
            return ;
        }
        int mid;getm;
        print(ls);
        print(rs);
    }
    
    void build(int l,int r,int v)
    {
        if(l==r){
            ma[v]=val[l];
            ma2[v]=-inf;
            return ;
        }
        int mid;getm;
        build(ls);
        build(rs);
        upda(v);
    }
    
    ll ask(int l,int r,int v,int x,int y,int z)
    {
        if(r<x||y<l)return -inf;
        if(x<=l&&r<=y){
            if(ma[v]!=z)return ma[v];
            return ma2[v];
        }
        int mid;getm;
        return max(ask(ls,x,y,z),ask(rs,x,y,z));
    }
    
    int main()
    {
        scanf("%d%d",&n,&m);
        for(int i=1;i<=m;++i)
            scanf("%d%d%lld",&edg[i].x,&edg[i].y,&edg[i].v);
        sort(edg+1,edg+m+1,cmp);
        for(int i=0;i<=n;++i)fa[i]=i;
        int cntt=0;
        for(int i=1;i<=m;++i)
        {
            int x=edg[i].x,y=edg[i].y;
            int fx=find(x),fy=find(y);
            if(fx!=fy){
                cntt++;
                pd[i]=1;
                fa[fx]=fa[fy];
                ans1+=edg[i].v;
                add(x,y,edg[i].v);
                add(y,x,edg[i].v);
            }
            if(cntt==n-1)break;
        }
        df1(1);
        dfs(1,1);
        build(1,num,1);
        ans2=inf;
        for(int i=1;i<=m;++i)
            if(!pd[i]){
                int x=edg[i].x,y=edg[i].y;
                ll tmp=-inf;
                while(top[x]!=top[y])
                {
                    if(dep[top[x]]>dep[top[y]]){
                        tmp=max(tmp,ask(1,num,1,dfn[top[x]],dfn[x],edg[i].v));
                        x=f[top[x]];
                    }else {
                        tmp=max(tmp,ask(1,num,1,dfn[top[y]],dfn[y],edg[i].v));
                        y=f[top[y]];
                    }
                }
                int lca=dep[x]<dep[y]?x:y,deper=dep[x]>dep[y]?x:y;
                if(x!=y)tmp=max(tmp,ask(1,num,1,dfn[son[lca]],dfn[deper],edg[i].v));
                ans2=min(ans2,ans1-tmp+edg[i].v);
            }
        printf("%lld",ans2);
    }

    还有一种类型题,本质上还是求次小生成树 

    【题目背景】

    HJZ 买了一套新房子,他正在为新房子的装修发愁。

    【题目描述】

    土豪 HJZ 的新房子需要铺设水管网。 水管网一共有 n 个点,其中 1 号点是进水口,其余 n − 1 个点是出水口。

    房子预留 了 m 根水管的空间,每根水管可以连接两个点,但要花费一定的代价。

    现在 HJZ 想知道让所有出水口都直接或间接与进水口联通的最小代价。

    由于装修 过程中可能会出现一些玄学事件,他还想知道代价最小的连接方案是否唯一。保证存在 一种连接方案。

    【输入格式】

    从文件 pipe.in 中读入数据。

    第一行一个正整数 T 表示测试数据组数。

    对于每一组测试数据:

    • 第一行两个整数 n, m 分别表示水管网的点数和预留的管道数。

    • 接下来 m 行,每行三个正整数 a, b, c 表示一条连接 a, b 的双向管道需要 c 的 代价。

    【输出格式】 输出到文件 pipe.out 中。 对于每一组测试数据,分别输出两行。

    第一行一个整数表示最小代价。 第二行一个字符串 Yes 或 No 表示连接方案是否唯一。 

    这道题当时考试的时候一时脑抽。。。就写出来了一个二分。。。留在这里当做纪念吧。。。

    //by 减维
    #include<cstdio>
    #include<iostream>
    #include<cstring>
    #include<queue>
    #include<cstdlib>
    #include<ctime>
    #include<cmath>
    #include<map>
    #include<bitset>
    #include<algorithm>
    #define ll long long
    using namespace std;
    
    struct lin{
        int fr,to,v;
    }ed[200005];
    
    struct edge{
        int to,ne,v;
    }e[200005];
    
    struct node{
        int v,pos;
    }a[100005];
    
    ll ans;
    int t,n,m,num,tot,ecnt,head[100005],f[100005],fa[100005],lx[100005],rx[100005],son[100005],siz[100005],top[100005],dfn[100005],dep[100005];
    bool fla,pd[200005];
    
    bool cmp(const lin&x,const lin&y){return x.v<y.v;}
    bool cm2(const node&x,const node&y){
        if(x.v==y.v)return x.pos<y.pos;
        return x.v<y.v;
    }
    
    void init()
    {
        ecnt=num=tot=0;
        ans=0;
        fla=0;
        memset(f,0,sizeof(f));
        memset(pd,0,sizeof(pd));
        memset(lx,0,sizeof(lx));
        memset(rx,0,sizeof(rx));
        memset(dep,0,sizeof(dep));
        memset(son,0,sizeof(son));
        memset(siz,0,sizeof(siz));
        memset(dfn,0,sizeof(dfn));
        memset(top,0,sizeof(top));
        memset(head,0,sizeof(head));
    }
    
    void add(int x,int y,int z)
    {
        e[++ecnt].to=y;
        e[ecnt].ne=head[x];
        e[ecnt].v=z;
        head[x]=ecnt;
    }
    
    int find(int x)
    {
        if(x==fa[x])return x;
        fa[x]=find(fa[x]);
        return fa[x];
    }
    
    void df1(int x)
    {
        siz[x]=1;
        dep[x]=dep[f[x]]+1;
        for(int i=head[x];i;i=e[i].ne)
        {
            int dd=e[i].to;
            if(dd==f[x])continue ;
            f[dd]=x;
            df1(dd);
            siz[x]+=siz[dd];
            if(!son[x]||siz[son[x]]<siz[dd])
                son[x]=dd;
        }
    }
    
    void dfs(int x,int tp)
    {
        top[x]=tp;
        dfn[x]=++num;
        if(son[x])dfs(son[x],tp);
        for(int i=head[x];i;i=e[i].ne)
        {
            int dd=e[i].to;
            if(dd==f[x]||dd==son[x])continue;
            dfs(dd,dd);
        }
    }
    
    void df2(int x,int v)
    {
        a[++tot].v=v;
        a[tot].pos=dfn[x];
        for(int i=head[x];i;i=e[i].ne)
        {
            int dd=e[i].to;
            if(dd==f[x])continue;
            df2(dd,e[i].v);
        }
    }
    
    bool check(int x,int y,int v)
    {
        int l=lx[v],r=rx[v];
        int mid;
        while(l<=r){
            mid=(l+r)>>1;
            if(a[mid].pos<=y&&a[mid].pos>=x)return 1;
            else if(a[mid].pos>y)r=mid-1;
            else l=mid+1;
        }
        return 0;
    }
    
    int main()
    {
        freopen("pipe.in","r",stdin);
        freopen("pipe.out","w",stdout);
        scanf("%d",&t);
        while(t--)
        {
            scanf("%d%d",&n,&m);
            init();
            for(int i=1;i<=m;++i)scanf("%d%d%d",&ed[i].fr,&ed[i].to,&ed[i].v);
            sort(ed+1,ed+m+1,cmp);
            for(int i=1;i<=n;++i)fa[i]=i;
            int cnt=0;
            for(int i=1,x,y,fx,fy;i<=m;++i)
            {
                x=ed[i].fr,y=ed[i].to;
                fx=find(x),fy=find(y);
                if(fx!=fy){
                    fa[fx]=fy;
                    cnt++;
                    ans+=(ll)ed[i].v;
                    add(x,y,ed[i].v);
                    add(y,x,ed[i].v);
                    pd[i]=1;
                }
                if(cnt==n-1)break;
            }
            printf("%lld
    ",ans);
            df1(1);
            dfs(1,1);
            df2(1,0);
            sort(a+1,a+tot+1,cm2);
            lx[a[1].v]=1;
            for(int i=1;i<=tot;++i)
                if(a[i].v!=a[i-1].v)lx[a[i].v]=i,rx[a[i-1].v]=i-1;
            rx[a[tot].v]=tot;
            for(int i=1,x,y,lca,v,deper;i<=m;++i)
                if(!pd[i])
                {
                    x=ed[i].fr,y=ed[i].to,v=ed[i].v;
                    while(top[x]!=top[y]){
                        if(dep[top[x]]<dep[top[y]])swap(x,y);
                        if(check(dfn[top[x]],dfn[x],v)){
                            fla=1;
                            break;
                        }
                        x=f[top[x]];
                    }
                    if(x!=y){
                        lca=dep[x]<dep[y]?x:y;
                        deper=lca==x?y:x;
                        if(check(dfn[son[lca]],dfn[deper],v))fla=1;
                    }
                    if(fla)break;
                }
            if(fla)printf("No
    ");
            else printf("Yes
    ");
        }
        return 0;
    }
  • 相关阅读:
    Inno Setup 6.0.4
    使用Microsoft Enterprise Library 5.0记录日志信息
    Log4net用法
    继续接着上一篇写:使用C#实现DHT磁力搜索的BT种子后端管理程序+数据库设计(开源)[搜片神器]
    磁力王:种子磁力搜索神器使用教程
    C# WebBrowser 网页缩放的方法
    Mysql5.7修改root密码教程
    【MAVEN】maven项目下载更新pom jar包速度慢 解决方案
    C# DataGridView自动保存列的宽度和位置
    Java实现敏感词过滤
  • 原文地址:https://www.cnblogs.com/rir1715/p/7681657.html
Copyright © 2011-2022 走看看