zoukankan      html  css  js  c++  java
  • 严格次小生成树

    题目大意:

    给定一个无向图,求出该图的次小生成树。

    点数n≤100 000 边数m≤300 000

    做法其实是比较简单的,首先我们求出给定图中的最小生成树,然后我们枚举每一条非树边,将其加入生成树中,可以证明这样一定会出现一个环,我们只需要在这个环中找到除去这条边以外最大的边,(又因为是严格次小生成树,所以还有找次小的,以防最大值与非树边边权相等),将它删去,然后得到另一个生成树,记录一下ans,对于所有非树边枚举得到的ans只要取一个最小值就可以了(因为是次小生成树)

    但是这种做法的复杂度有点大,我们来分析一下:求最小生成树是O(n),(由于并查集时间复杂度可以看作是一个常数),枚举非选择的边是O(m),每一次求最大值(和次大值)也是O(m),所以渐近时间复杂度是O(m^2),如果要通过这道题还是差了很多。

    那么我们进行优化:

    很显然最小生成树不能优化,每一条非树边都必须被枚举,所以我们只能在求最大值上下手了。

    考虑到我们在加入该非树边之前应该是一颗没有环的树,那么求一段路径上的最大值我们是会做的,树链剖分?代码量过于大了,那么我们考虑另一种做法:树上倍增LCA

    其实LCA可以干很多事情的。。。

    关于LCA求树上两点路径中边权最大值,这个其实是比较简单的,我们在原来的基础上开一个w[u][i],表示从u向上跳2^i个点,这一段路径的最大值

    动态转移方程也是比较显然的:我们假定f[u][i]表示从u向上跳2^i个点到达点的坐标,那么就有w[u][i+1]=max(w[u][i],w[f[u][i]][i])

    所以我们只需要就从这两个点分别跳到他们的LCA处,每一次取最大值就可以了,这样我们就将m^2的算法转化成了mlogn,这样通过这道题就轻松加愉快了

    但是我们忽略了一点:

    题目中要求严格次小,但是这种做法很有可能就是和最小生成树相等,那么我们还需要解决这个难题。

    其实也不算是难题,只是在原来的基础上多记录一个次大值,我们假设w2[u][i]表示从u向上跳2^i个单位,这段路径上的次大值。

    动态转移方程就是:当w[u][i]>w[f[u][i]][i]  w2[u][i+1]=max(w[f[u][i]][i],w2[f[u][i]][i])

             当w[u][i]<w[f[u][i]][i]  w2[u][i+1]=max(w[u][i],w2[f[u][i]][i])

             当相等时,就继承上一个段的就可以了

    那么我们就圆满的解决了这道题,总复杂度:mlogn+n*k  k为一个比较小的常数

    还有一些注意事项:

    1.inf要足够大  因为这个边权之和很大很大

    2.建树时要建反向边,谁知道给定的两个点相对顺序呢?

    最后,附上本题代码:

      1 #include<cstdio>
      2 #include<algorithm>
      3 #include<iostream>
      4 using namespace std;
      5 //Debug Yufenglin
      6 #define dej printf("Running
    ");
      7 #define dep1(x) cout<<#x<<"="<<x<<endl;
      8 #define dep2(x,y) cout<<#x<<"="<<x<<' '<<#y<<"="<<y<<endl;
      9 #define dep3(x,y,z) cout<<#x<<"="<<x<<' '<<#y<<"="<<y<<' '<<#z<<"="<<z<<endl;
     10 
     11 //Standfor Yufenglin
     12 #define LL long long
     13 #define LB long double
     14 #define reg register
     15 #define il inline
     16 #define inf 1e8
     17 #define maxn 100000
     18 #define maxm 300000
     19 
     20 struct EDGE
     21 {
     22     LL u,v,w;
     23 };
     24 struct TREE
     25 {
     26     LL to,val,nxt;
     27 };
     28 TREE tree[maxm+5];
     29 EDGE edge[maxm+5];
     30 LL n,m;
     31 LL fa[maxn+5],f[maxn+5][30],w1[maxn+5][30],maxw1,maxw2,cnt,num;
     32 LL head[maxn+5],dep[maxn+5],w2[maxn+5][30],ans,minans=1e16,sum;
     33 bool vis[maxm+5];
     34 
     35 bool cmp(EDGE x,EDGE y)
     36 {
     37     return x.w<y.w;
     38 }
     39 void add(LL x,LL y,LL z)
     40 {
     41     edge[++cnt].u=x;
     42     edge[cnt].v=y;
     43     edge[cnt].w=z;
     44 }
     45 void addedge(LL x,LL y,LL z)
     46 {
     47     tree[++num].to=y;
     48     tree[num].val=z;
     49     tree[num].nxt=head[x];
     50     head[x]=num;
     51 }
     52 LL find(LL x)
     53 {
     54     if(fa[x]==x) return x;
     55     return fa[x]=find(fa[x]);
     56 }
     57 void dfs(LL u,LL fa)
     58 {
     59     dep[u]=dep[fa]+1;
     60     f[u][0]=fa;
     61     for(int i=0; i<=25; i++)
     62     {
     63         f[u][i+1]=f[f[u][i]][i];
     64         w1[u][i+1]=max(w1[u][i],w1[f[u][i]][i]);
     65         w2[u][i+1]=max(w2[u][i],w2[f[u][i]][i]);
     66         if(w1[u][i]>w1[f[u][i]][i]) w2[u][i+1]=max(w2[u][i+1],w1[f[u][i]][i]);
     67         if(w1[u][i]<w1[f[u][i]][i]) w2[u][i+1]=max(w2[u][i+1],w1[u][i]);
     68     }
     69     for(int i=head[u]; i; i=tree[i].nxt)
     70     {
     71         if(tree[i].to==fa) continue;
     72         w1[tree[i].to][0]=tree[i].val;
     73         dfs(tree[i].to,u);
     74     }
     75 }
     76 LL LCA(LL x,LL y)
     77 {
     78     if(dep[x]<dep[y]) swap(x,y);
     79     for(int i=25; i>=0; i--)
     80     {
     81         if(dep[f[x][i]]>=dep[y])
     82         {
     83             x=f[x][i];
     84         }
     85         if(x==y) return x;
     86     }
     87     for(int i=25; i>=0; i--)
     88     {
     89         if(f[x][i]!=f[y][i])
     90         {
     91             x=f[x][i];
     92             y=f[y][i];
     93         }
     94     }
     95     return f[x][0];
     96 }
     97 LL query(LL x,LL y,LL d)
     98 {
     99     LL anst=-1;
    100     for(int i=25;i>=0;i--)
    101     {
    102         if(dep[f[x][i]]>=dep[y])
    103         {
    104             if(d!=w1[x][i]) anst=max(anst,w1[x][i]);
    105             else anst=max(anst,w2[x][i]);
    106             x=f[x][i];
    107         }
    108     }
    109     return anst;
    110 }
    111 int main()
    112 {
    113     scanf("%lld%lld",&n,&m);
    114     for(int i=1; i<=m; i++)
    115     {
    116         LL x,y,z;
    117         scanf("%lld%lld%lld",&x,&y,&z);
    118         add(x,y,z);
    119     }
    120     sort(edge+1,edge+cnt+1,cmp);
    121     for(int i=1; i<=n; i++) fa[i]=i;
    122     for(int i=1; i<=m; i++)
    123     {
    124         if(find(edge[i].u)!=find(edge[i].v))
    125         {
    126             addedge(edge[i].u,edge[i].v,edge[i].w);
    127             addedge(edge[i].v,edge[i].u,edge[i].w);
    128             fa[find(edge[i].u)]=find(edge[i].v);
    129             ans+=edge[i].w;
    130             vis[i]=1;
    131             sum++;
    132         }
    133         if(sum==n-1) break;
    134     }
    135     for(int i=1;i<=n;i++) w2[i][0]=-1;
    136     dfs(1,0);
    137     for(int i=1; i<=m; i++)
    138     {
    139         if(vis[i]==0)
    140         {
    141             LL lca=LCA(edge[i].u,edge[i].v);
    142             LL maxu=query(edge[i].u,lca,edge[i].w);
    143             LL maxv=query(edge[i].v,lca,edge[i].w);
    144             minans=min(minans,ans-max(maxu,maxv)+edge[i].w);
    145         }
    146     }
    147     printf("%lld
    ",minans);
    148     return 0;
    149 }
  • 相关阅读:
    [NOIp2009] $Hankson$ 的趣味题
    [洛谷P1730] 最小密度路径
    [NOIp2015] 运输计划
    [NOIp2012] 借教室
    [NOIp2012] 国王游戏
    [NOIp2016] 蚯蚓
    [洛谷P1272] 重建道路
    [洛谷P1273] 有线电视网
    [ZJOI2010] 数字计数
    ☆ [HDU2089] 不要62「数位DP」
  • 原文地址:https://www.cnblogs.com/yufenglin/p/10657475.html
Copyright © 2011-2022 走看看