学会用bitset乱搞
bitset的基本用法
介绍
bitset
是一个存储 \(0/1\) 的容器,但是它的储存是以 bit 为单位的,内存仅为一个 \(bool\) 类型变量的 \(\frac{1}{8}\) ,因此其时空间复杂度都很优秀
要想使用 bitset
,首先需要引用头文件 #include<bitset>
bitset
模板类的定义为:
template <size_t N>
class bitset
{
...
};
其中 size_t N
是无符号整型,在使用时 N 必须为一个整型常数
拿 bitset<10>s
举例,这是一个由 \(10\) 位 \(0/1\) 组成的容器,下标从 \(0\) 开始,最低位在最右端
声明
常见有三种声明方式
-
bitset<10> s;//bitset() cout << s << endl;//0000000000
当这种情况时,
bitset
中的每一位都默认为 \(0\) -
bitset<10> s(10);//bitset(unsigned long val) cout << s << endl;//0000001010
当填入数字时,会将其转化为二进制形式,当数字最高位超出
bitset
大小时,会抛弃高位,只保留低位 -
string str = "10110"; bitset<10> s(str);//bitset(const string &s) cout << s << endl;//0000010110
当传入一个 \(01\) 串时,是按照低位对齐将其转化的,同样当 \(str.size()\) 过大时,也是抛弃高位保留低位的
运算符
以下以 bitset<10> s(10)
和 bitset<10> t(7)
举例
-
operator []//访问指定位 cout << s[1] << endl;//1
-
operator ==/!= //判断两个 bitset 是否相同 cout << (s == t) << endl;//0
-
operator ^/^= //bitset之间执行异或运算 cout << (s ^ t) << endl;//0000001101
-
operator |/|= //bitset之间执行或运算 cout << (s | t) << endl;//0000001111
-
operator &/&= //bitset之间执行与运算 cout << (s & t) << endl;//0000000010
-
operator ~//将bitset中每一位全部01反转 cout << ~s << endl;//1111110101
常用的成员函数
以下以 bitset<10> s(10)
举例
-
count()
返回 \(1\) 的数量cout << s.count() << endl;//2
-
all()
若全部为都为 \(1\) 返回true
cout << s.all() << endl;//0
-
a.
set()
将整个bitset
设置为 \(1\)s.set(); cout << s << endl;//1111111111
b.
set(pos , val = true)
将某一位设置为指定值s.set(9, 1); cout << s << endl;//1000001010
-
a.
reset()
将整个bitset
设置为 \(0\)s.reset(); cout << s << endl;//0000000000
b.
reset(pos)
将某一位设置为 \(0\)s.reset(1); cout << s << endl;//0000001000
-
a.
flip()
将整个bitset
全部 \(01\) 翻转,与取反类似s.flip(); cout << s << endl;//1111110101
b.
flip(pos)
将某一位翻转s.flip(1); cout << s << endl;//0000001000
-
to_string()
将其转化为string
cout << s.to_string() << endl;//0000001010
_Find_first()
返回bitset
第一个 \(1\) 的下标,若没有的话返回其大小
cout << s._Find_first() << endl;//1
_Find_next(pos)
返回第一个严格大于pos
的 \(1\) 位置的下标,若没有的话返回其大小
cout << s._Find_next(1) << endl;//3
bitset的应用
-
题意
一个 \(n\) 个点的有向图,其中有 \(m1\) 条边一定存在, 有 \(m_2\) 条边一定不存在,询问两点之间是否一定(可能)到达
做法
明显是个裸的 Floyd ,但是 \(n=10^3\) 显然写不了 \(O(n^3)\) 的时间复杂度的算法,因此可以考虑用
bitset
优化,将时间复杂度降至 \(O(\frac{n^3}{w})\) ,其中 \(w\) 为计算机位数,一般取 \(64\)代码
void solve() { bitset<N> e1[N], e2[N]; // 1必到 2可能 int n, m1, m2, q; cin >> n >> m1 >> m2 >> q; for (int i = 1; i <= n; i++) e1[i].reset(), e2[i].set(), e1[i][i] = 1;//自已一定能到自己! for (int i = 1; i <= m1; i++) { int u, v; cin >> u >> v; e1[u][v] = 1; } for (int i = 1; i <= m2; i++) { int u, v; cin >> u >> v; e2[u][v] = 0; } for (int i = 1; i <= n; i++) for (int j = 1; j <= n; j++) { if (e1[j][i]) e1[j] |= e1[i]; if (e2[j][i]) e2[j] |= e2[i]; } while (q--) { int x, y; cin >> x >> y; cout << (e1[x][y] ? "Yes" : "No") << " "; cout << (e2[x][y] ? "Yes" : "No") << "\n"; } }
-
题意
给定 \(n\) 个数,询问是否存在方案使得选出一些数字为 \(3600\) 的倍数
做法
考虑常规做法,当 \(n\geq 3600\) 时由容斥可得必定存在,当 \(n<3600\) 时直接做01背包即可
考虑
bitset
做法,可以开一个大小为 \(7200\) 的bitset
状压,对于输入进来的数先取模,设取模后为 \(x\) ,可得若 \(f[i]=1\) 那么 \(f[i+x]=1\) ,该转移可以通过bitset
或上自身左移 \(x\) 位又因为 \(i+x\) 可能大于模数,所以还需要或上
bitset
右移 \(3600-x\) 位代码
void solve() { bitset<N> s; int n; cin >> n; for (int i = 1; i <= n; i++) { int x; cin >> x; x %= 3600; s |= (s << x) | (s >> (3600 - x)); s[x] = 1; } cout << (s[0] ? "YES" : "NO") << "\n"; }