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;
}
传送门: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;
}