bitset在图论上的应用 传递闭包【例题gym 100342J & gym 100345H 】

来源:互联网 发布:菜鸟网络航空港区地址 编辑:程序博客网 时间:2024/06/06 12:30

bitset优点:

bitset在某些常数优化以及状态保存方面被称之为神器并不为过,主要表现在以下几个方面:1. 状态表示。试想,用一个数来表示状态的极限是64位,而bitset可以保存任意位二进制数,并且修改简单,统计方便,并且支持批量操作。2. 常数优化。图论的题,尤其涉及不带权的邻接图,算法经常动辄 n2,n3 ,这个时候我们可以用n个bitset存储每个点的邻接情况,并进行相应位操作,来替代直接遍历图的操作,经常可以将总复杂度降低为原来的1/32甚至还要少!3. 集合运算。交集按位与,并集按位或,取反直接flip,简直好用。4. 存储优化。一个bool型变量占一个字节(1byte),而bitset的一位只占1bit!某些内存卡得特别紧的情况可以试试(一般无卵用=_=

bitset基本操作表:


下面通过gym上的两道例题来了解一下bitset在图论上的应用


传送门:gym 100342J Triatrip

题意:

给一个邻接矩阵,求有多少条路径可以由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;}


传送门:gym 100345H Settling the Universe Up

题意:

输入一个邻接矩阵,统计图中有多少对点 ( 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;}


0 0