1. 程式人生 > >bitset在圖論上的應用 傳遞閉包【例題gym 100342J & gym 100345H 】

bitset在圖論上的應用 傳遞閉包【例題gym 100342J & gym 100345H 】

bitset優點:

bitset在某些常數優化以及狀態儲存方面被稱之為神器並不為過,主要表現在以下幾個方面:
1. 狀態表示。試想,用一個數來表示狀態的極限是64位,而bitset可以儲存任意位二進位制數,並且修改簡單,統計方便,並且支援批量操作。
2. 常數優化。圖論的題,尤其涉及不帶權的鄰接圖,演算法經常動輒 n2,n3 ,這個時候我們可以用n個bitset儲存每個點的鄰接情況,並進行相應位操作,來替代直接遍歷圖的操作,經常可以將總複雜度降低為原來的1/32甚至還要少!
3. 集合運算。交集按位與,並集按位或,取反直接flip,簡直好用。
4. 儲存優化。一個bool型變數佔一個位元組(1byte),而bitset的一位只佔1bit!某些記憶體卡得特別緊的情況可以試試(一般無卵用=_=

bitset基本操作表:


下面通過gym上的兩道例題來了解一下bitset在圖論上的應用

題意:

給一個鄰接矩陣,求有多少條路徑可以由A出發,經過B ,再經過C,最後回到A。

思路:

無論是用鄰接矩陣乘法,還是跑flyod,都是 n3 的演算法,而n≤1500,直接來果斷TLE。
路徑為A→B→C→A,是一個三元環,我們列舉每一條B→C路徑,如果用n個bitset儲存每一個點的出度與入度情況,然後將B的入度與C的出度相與,統計相與後的結果有多少個1,便是有多少個三元環,累計相與結果,答案即為累計值/3,複雜度下降為O(n2∗n/32),可以卡著時間過。
之所以要除以3的原因是環A→B→C→A包含了B→C→A→B,與C→A→B→C。

程式碼:

#include <bits/stdc++.h>
using  namespace  std;

#define ll __int64
#define rep(i,k,n) for(int i=k;i<=n;i++)

const int N=1510;

char s[N][N];
bitset<N>bit1[N], bit2[N], tmp;
int n;

int  main(){
  freopen("triatrip.in","r",stdin);//必須加上,不然得WA
  freopen("triatrip.out","w",stdout);
  scanf("%d", &n);
  rep(i, 0, n-1){
    scanf("%s", s[i]);
    rep(j, 0, n-1){
      if(s[i][j] == '+')bit1[i].set(j), bit2[j].set(i);
    }
  }

  ll ans=0;
  rep(i, 0, n-1){
    rep(j, 0, n-1){
      if(bit1[i][j]){
        tmp = bit1[j] & bit2[i];
        ans += tmp.count();
      }
    }
  }
  printf("%I64d\n", ans / 3);
  return 0;
}


題意:

輸入一個鄰接矩陣,統計圖中有多少對點 ( u , v ) 是從u出發可以到達v,且u < v, 單向。
同時動態增刪圖中路徑。


思路:

這題用線段樹維護起來有些麻煩
先用bitset1儲存每個點一次可到達點。
然後新開一組bitset2儲存每個點的所有升序可到達點,bitset2可由bitset1進行或運算推出來,每次更新都重新推一次,直接暴力即可。
不得不說bitset實在神奇。。不用bitset我都不知道怎麼做這道題了。

程式碼:

#include <bits/stdc++.h>
#define rep(i,k,n) for(int i=k;i<=n;i++)
using  namespace  std;

template<class T> void read(T&num) {
    char CH; bool F=false;
    for(CH=getchar();CH<'0'||CH>'9';F= CH=='-',CH=getchar());
    for(num=0;CH>='0'&&CH<='9';num=num*10+CH-'0',CH=getchar());
    F && (num=-num);
}
int stk[70], tp;
template<class T> inline void print(T p) {
    if(!p) { puts("0"); return; }
    while(p) stk[++ tp] = p%10, p/=10;
    while(tp) putchar(stk[tp--] + '0');
    putchar('\n');
}

const int N=210;

int n, m, k, sum;
bitset<N>b[N], bit[N];

void update(){
  rep(i, 1, n)bit[i].reset();
  sum=0;
  for(int i=n; i>=1; i--){
    bit[i].set(i);
    for(int j=1; j<i; j++){
      if(b[j][i])
        bit[j] |= bit[i];
    }
    sum += bit[i].count();
  }
}

int  main(){
  freopen("settling.in", "r", stdin);
  freopen("settling.out", "w", stdout);
  read(n),read(m);
  rep(i, 1, m){
    int u, v;
    read(u),read(v);
    b[u].set(v);
  }
  update();
  print(sum-n);//總數減去自環

  read(k);
  while(k--){
    char op[2];int u, v;
    scanf("%s%d%d",op, &u, &v);
    if(op[0] == '+'){
      b[u].set(v);
      update();
      print(sum-n);
    }
    else if(op[0] == '-'){
      b[u].reset(v);
      update();
      print(sum-n);
    }
    else{
      if(bit[u].test(v))puts("YES");
      else puts("NO");
    }
  }
  return 0;
}