bitset在圖論上的應用 傳遞閉包【例題gym 100342J & gym 100345H 】
阿新 • • 發佈:2018-12-24
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;
}