天天看點

boruvka算法學習筆記

boruvka算法學習筆記

大概就是:初始每個點都是獨立的集合,每次merge過程從所有獨立集合出發,找到一條權值最小(權值相同則編号)最小的連向其它集合的邊,然後合并集合。顯然每次都會使得集合數減少一半,是以合并次數是log級别的。

boruvka算法學習筆記

例題:

luogu3366mst模闆題

直接模拟算法過程即可。。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 2e5 + 10;
#define fi first
#define se second
#define pb push_back
#define wzh(x) cerr<<#x<<'='<<x<<endl;
int n,m;
int s[N],t[N],w[N];
struct Boruvka{
  int f[N],use[N],best[N];
  void init(){
    for(int i=1;i<=n;i++)f[i]=i,best[i]=0;
    for(int i=1;i<=m;i++)use[i]=0;
  }
  int find(int x){
    return f[x]==x?x:(f[x]=find(f[x]));
  }
  int better(int x,int y){
    if(!y)return 1;
    if(w[x]<w[y])return 1;
    return x<y;
  }
  LL get(){
    int merge=0;
    LL sum=0;
    while(merge!=n-1){
      for(int i=1;i<=m;i++){
        if(use[i])continue;
        int x=find(s[i]),y=find(t[i]);
        if(x==y)continue;
        if(better(i,best[x]))best[x]=i;
        if(better(i,best[y]))best[y]=i;
      }
      for(int i=1;i<=n;i++){
        if(best[i]){
          int x=find(s[best[i]]),y=find(t[best[i]]);
          if(x!=y){
            use[best[i]]=1;
            f[x]=y;
            merge++;
            sum+=w[best[i]];
          }
          best[i]=0;
        }
      }
    }
    return sum;
  }
}g;
int main() {
  ios::sync_with_stdio(false);
  cin>>n>>m;
  for(int i=1;i<=m;i++)cin>>s[i]>>t[i]>>w[i];
  g.init();
  cout<<g.get()<<'\n';
  return 0;
}
           

cf888G

把所有值建一個01字典樹,然後我們貪心的考慮生成樹的過程,因為跟二進制有關系,是以從大到小貪心是合法的。我們從字典樹的一個節點考慮,節點的左右兒子必然不同為一個集合。而一個兒子節點代表的集合連出去的最小權值邊,必然是跟另一個兒子節點連才是最優的(二進制異或的性質)。那每次遞歸處理左右兒子,然後再合并左右兒子代表的集合就可以了。合并左右兒子的時候運用啟發式合并的思想,枚舉size小的一邊,在另一個兒子節點代表子樹上查異或最小的值。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 6e6 + 10;
#define fi first
#define se second
#define pb push_back
#define wzh(x) cerr<<#x<<'='<<x<<endl;
int n,a[N];
vector<int>v[N];
struct trie{
  int cnt,t[N][2],ta[N];
  void insert(int z,int x,int y){
    v[z].pb(y);
    if(x<0){
      return ;
    }
    bool st=1<<x&y;
    if(!t[z][st])t[z][st]=++cnt;
    insert(t[z][st],x-1,y);
  }
  int query(int z,int x,int y){
    if(x<0)return 0;
    bool st=1<<x&y;
    if(t[z][st]) {
      return query(t[z][st], x - 1, y);
    } else {
      return query(t[z][st ^ 1], x - 1, y) + (1 << x);
    }
  }
}g;
LL gao(int o,int q){
  if(v[o].size()<=1)return 0;
  LL ans=0;
  for(int i=0;i<2;i++){
    if(g.t[o][i]){
      ans+=gao(g.t[o][i],q-1);
    }
  }
  if(!g.t[o][0]||!g.t[o][1])return ans;
  int x=g.t[o][0],y=g.t[o][1];
  if(v[x].size()>v[y].size())swap(x,y);
  LL cm=1e18;
  for(auto k:v[x]){
    cm=min(cm,1ll*g.query(y,q-1,k));
  }
  ans+=cm+(1<<q);
  return ans;
}
int main() {
  ios::sync_with_stdio(false);
  cin>>n;g.cnt=1;
  for(int i=1;i<=n;i++)cin>>a[i];
  //字典樹上,兩個不同子樹必然屬于不同的聯通快,需要用一個最小權值的邊連一起,可以用啟發式合并的
  //思想,用小的到大的裡面枚舉。。。
  for(int i=1;i<=n;i++)g.insert(1,30,a[i]);
  cout<<gao(1,30)<<'\n';
  return 0;
}
//200000*30 
           

2020牛客多校5B

因為題目要求每時每刻必須聯通,且環上異或為0,那麼任意兩點的連起來的邊權都是固定的。

(x,y)的邊權=(1-x)的值^(1-y)的值,把每個點z的值 都變成(1-z)路徑上邊權的異或和。

然後就跟上題一模一樣了。

#include <bits/stdc++.h>
using namespace std;
typedef long long LL;
const int N = 3e6 + 10;
#define fi first
#define se second
#define pb push_back
#define wzh(x) cerr<<#x<<'='<<x<<endl;
int n,a[N];
vector<int>v[N];
struct trie{
  int cnt,t[N][2];
  void insert(int z,int x,int y){
    v[z].pb(y);
    if(x<0){
      return ;
    }
    bool st=1<<x&y;
    if(!t[z][st])t[z][st]=++cnt;
    insert(t[z][st],x-1,y);
  }
  int query(int z,int x,int y){
    if(x<0)return 0;
    bool st=1<<x&y;
    if(t[z][st]) {
      return query(t[z][st], x - 1, y);
    } else {
      return query(t[z][st ^ 1], x - 1, y) + (1 << x);
    }
  }
}g;
LL gao(int o,int q){
  if(v[o].size()<=1)return 0;
  LL ans=0;
  for(int i=0;i<2;i++){
    if(g.t[o][i]){
      ans+=gao(g.t[o][i],q-1);
    }
  }
  if(!g.t[o][0]||!g.t[o][1])return ans;
  int x=g.t[o][0],y=g.t[o][1];
  if(v[x].size()>v[y].size())swap(x,y);
  LL cm=1e18;
  for(auto k:v[x]){
    cm=min(cm,1ll*g.query(y,q-1,k));
  }
  ans+=cm+(1<<q);
  return ans;
}
const int M=1e5+1;
vector<pair<int,int>>G[N];
int vis[N];
void dfs(int x,int t=0,int pre=0){
  a[x]=t;
  for(auto k:G[x]){
    if(k.fi!=pre){
      dfs(k.fi,t^k.se,x);
    }
  }
}
int main() {
  ios::sync_with_stdio(false);
  cin>>n;g.cnt=1;
  for(int i=1;i<n;i++){
    int s,t,w;
    cin>>s>>t>>w;
    ++s;++t;
    G[s].pb({t,w});
    G[t].pb({s,w});
  }
  dfs(1);
  for(int i=1;i<=n;i++)g.insert(1,30,a[i]);
  cout<<gao(1,30)<<'\n';
  return 0;
}
//200000*30