1. 程式人生 > >藍書(演算法競賽進階指南)刷題記錄——BZOJ1977次小生成樹(樹上倍增)

藍書(演算法競賽進階指南)刷題記錄——BZOJ1977次小生成樹(樹上倍增)

題目:BZOJ1977.

題目大意:給定一張無向圖,求這張無向圖的嚴格次小生成樹.

首先對於次小生成樹,我們可以發現一定有解滿足是在最小生成樹的基礎上更改一條邊得來的.

那麼我們先求出最小生成樹,並考慮如何求出更改的邊使得這棵生成樹為嚴格次小生成樹.

我們稱最小生成樹上的變為樹邊,其它邊為非樹邊.那麼很顯然對於一條非樹邊(x,y),我們嘗試用這條邊更改一條邊,那麼換掉的邊一定是在最小生成樹上的鏈(x,y)上的.由於滿足嚴格次優,所以更改的邊一定是這條鏈上最大的與這條非樹邊權值不相等的邊.

有了這一條性質,我們就可以考慮在這棵最小生成樹上進行一些操作,支援快速求出這棵最小生成樹的一條路徑上的最大邊與次大邊(有可能最大邊與一條非樹邊相等).

那麼在樹上倍增求解這個問題(當然也可以用樹剖做)即可.時間複雜度O(mlogn),空間複雜度O(nlogn).

程式碼如下:

#include<bits/stdc++.h>
  using namespace std;
#define Abigail inline void
typedef long long LL;
const int N=100000,M=300000,C=20;
const LL INF=(1LL<<50LL)-1;
int n,m;
struct graph_side{
  int x,y,flag;
  LL v;
  bool operator < (const graph_side p)const{return v<p.v;}
}ge[M+9];
int fa[N+9];
struct tree_side{
  int y,next;
  LL v;
}te[N*2+9];
int lin[N+9],top;
int gr[C][N+9],deep[N+9];
LL val1[C][N+9],val2[C][N+9],ans=INF,sum;
int get(int u){return fa[u]==u?u:fa[u]=get(fa[u]);}
void ins(int x,int y,LL v){te[++top].y=y;te[top].v=v;te[top].next=lin[x];lin[x]=top;}
void Update_val1(LL &s1,LL v){s1=max(s1,v);}
void Update_val2(LL &s2,LL s1,LL v1,LL v2){
  if (s1<v1) s2=s1,s1=v1;
  else if (s1>v1) s2=max(s2,v1);
  if (s2<v2) s2=v2;
}
void dfs(int k){
  deep[k]=deep[gr[0][k]]+1;
  for (int i=1;i<C;++i){
    gr[i][k]=gr[i-1][gr[i-1][k]];
    Update_val1(val1[i][k],max(val1[i-1][k],val1[i-1][gr[i-1][k]]));
    Update_val2(val2[i][k],val1[i][k],min(val1[i-1][k],val1[i-1][gr[i-1][k]]),max(val2[i-1][k],val2[i-1][gr[i-1][k]]));
  }
  for (int i=lin[k];i;i=te[i].next)
    if (te[i].y^gr[0][k]){
      gr[0][te[i].y]=k;
      val1[0][te[i].y]=te[i].v;
      val2[0][te[i].y]=-INF;
      dfs(te[i].y);
    }
}
LL Query_val1(int x,int y){
  LL s1=-INF;
  if (deep[x]<=deep[y]) swap(x,y);
  for (int i=C-1;i>=0;--i)
    if (deep[gr[i][x]]>=deep[y]){
      Update_val1(s1,val1[i][x]);
      x=gr[i][x];
    }
  if (x==y) return s1;
  for (int i=C-1;i>=0;--i)
    if (gr[i][x]^gr[i][y]){
      Update_val1(s1,max(val1[i][x],val1[i][y]));
      x=gr[i][x];y=gr[i][y];
    }
  Update_val1(s1,max(val1[0][x],val1[0][y]));
  return s1;
}
LL Query_val2(int x,int y){
  LL s1=-INF,s2=-INF,M;
  if (deep[x]<=deep[y]) swap(x,y);
  for (int i=C-1;i>=0;--i)
    if (deep[gr[i][x]]>=deep[y]){
      Update_val1(s1,val1[i][x]);
      Update_val2(s2,s1,val1[i][x],val2[i][x]);
      x=gr[i][x];
    }
  if (x==y) return s2;
  for (int i=C-1;i>=0;--i)
    if (gr[i][x]^gr[i][y]){
      Update_val1(s1,max(val1[i][x],val1[i][y]));
      Update_val2(s2,s1,val1[i][x],val2[i][x]);
      Update_val2(s2,s1,val1[i][y],val2[i][y]);
      x=gr[i][x];y=gr[i][y];
    }
  Update_val1(s1,max(val1[0][x],val1[0][y]));
  Update_val2(s2,s1,val1[0][x],val2[0][x]);
  Update_val2(s2,s1,val1[0][y],val2[0][y]);
  return s2;
}
Abigail into(){
  scanf("%d%d",&n,&m);
  int x,y;
  LL v;
  for (int i=1;i<=m;++i){
    scanf("%d%d%lld",&x,&y,&v);
    ge[i].x=x;ge[i].y=y;ge[i].v=v;
  }
}
Abigail work(){
  sort(ge+1,ge+1+m);
  for (int i=1;i<=n;++i) fa[i]=i;
  int x,y;
  for (int i=1;i<=m;++i){
    x=get(ge[i].x);y=get(ge[i].y);
    if (x==y) continue;
    fa[x]=y;
    ins(ge[i].x,ge[i].y,ge[i].v);ins(ge[i].y,ge[i].x,ge[i].v);
    ge[i].flag=1;
    sum+=ge[i].v;
  }
  dfs(1);
  LL v;
  for (int i=1;i<=m;++i)
    if (!ge[i].flag&&ge[i].x^ge[i].y){
      v=Query_val1(ge[i].x,ge[i].y);
      if (v==ge[i].v) ans=min(ans,sum-Query_val2(ge[i].x,ge[i].y)+ge[i].v);
      else ans=min(ans,sum-v+ge[i].v);
    }
}
Abigail outo(){
  printf("%lld\n",ans);
}
int main(){
  into();
  work();
  outo();
  return 0;
}