严格次小生成树

题目大意:

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

点数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\n");
  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\n",minans);
148     return 0;
149 }

 

posted @ 2019-04-04 23:02  于丰林  阅读(310)  评论(0编辑  收藏  举报