Bitset

Bitset是什么

我们常常用一个数字的二进制表现一个集合中的子集,但是由于我们最大常用数字也就是longlong,所以能表示的集合大小是比较小的。
大集合我们怎么表示呢?在空间压缩的不太紧张的情况下,一般都是选择用数组来替代位,这样理解起来很直观。但有个缺点,我们空间浪费十分严重,而且没法快速做集合运算,如异或、与、或等。
bitset应运而生,它的每个位只占一个bit,这为我们大大节省了空间。而且它也支持如同数字之间的与、或、异或操作。

stl

我们看一下C++的stl中的bitset,它实现了一个类似我们上面所描述的那样的功能。
我们可以参考C++Refence来使用它。最常用的几个函数有:

1
2
3
4
5
6
bitset<size> bt; //constructor function
bt[i]; //get bit
bt.count(); //get the number of "1" in bitset
bt.set(i); //set the position i to "1"
bt.reset(); //reset all bits
bt.flip(); //flip some position

优化

学习stl的最好方法就是多用它,做完几道模板题之后我们发现,这样的结果虽然能做对,但是速度并不是很高效,在vjudge上看看别人的代码,发现很多人都自己写了一套bitset,我们意识到,或许C++给我们的bitset并不是那么优秀。
我们可以自己写一套用int去压二进制位实现bitset的模板。

手写bitset

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
  #include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<iostream>
using namespace std;
typedef long long ll;
#ifndef ONLINE_JUDGE
#define dbg(x...) do{cout << "\033[33;1m" << #x << "->" ; err(x);} while (0)
void err(){cout << "\033[39;0m" << endl;}
template<template<typename...> class T, typename t, typename... A>
void err(T<t> a, A... x){for (auto v: a) cout << v << ' '; err(x...);}
template<typename T, typename... A>
void err(T a, A... x){cout << a << ' '; err(x...);}
#else
#define dbg(...)
#endif
#define inf 1ll << 50
const int SN = 1005;
const int N = 5e5 + 5;

unsigned int BitReverse(unsigned int x)
{
static unsigned int a[1 << 16 | 1], now = 1;
if(now) {
a[0] = now = 0;
for (int i = 1; i < 1 << 16; i++)
a[i] = (a[i >> 1] >> 1) | ((i & 1) << 15);
}
return a[x >> 16] | (a[x & 65535] << 16);
}

int BitNum(unsigned int x)
{
static unsigned int a[1 << 16 | 1], now = 1;
if (now)
{
a[0] = now = 0;
for (int i = 1; i < 1 << 16; i++)
a[i] = a[i >> 1] + (i & 1);
}
return a[x >> 16] + a[x & 65535];
}

struct Bitset {
static const int BIT = 32;
static const int SIZE = SN / BIT + 1;
unsigned int a[SIZE];

Bitset();

unsigned int & operator [] (const int);
const unsigned int & operator [] (const int) const;

Bitset operator & (const Bitset &) const;
Bitset operator | (const Bitset &) const;
Bitset operator ^ (const Bitset &) const;
Bitset operator << (int) const;
Bitset operator >> (int) const;

Bitset & operator &= (const Bitset &);
Bitset & operator |= (const Bitset &);
Bitset & operator ^= (const Bitset &);
Bitset & operator <<= (int);
Bitset & operator >>= (int);

Bitset operator ~ () const;

// reverse the binary bit in [0, x)
// "000110010".Reverse(5) = "000001001"
Bitset Reverse(int x) const ;

// set the xth binary bit value to f
void Set(int x, bool f = true);

// set all binary bit value to 0
void Clear();

// return the xth binary bit value
bool CheckBit(int x) const ;

// return the number of one in binary bit value
int AllBit() const;

void Flip(int x);
}s[N];

Bitset::Bitset()
{
memset(a, 0, sizeof a);
}

unsigned int & Bitset::operator [] (const int x)
{
return a[x];
}

const unsigned int & Bitset::operator [] (const int x) const
{
return a[x];
}

Bitset Bitset::operator & (const Bitset &x) const
{
Bitset ans;
for (int i = 0; i < SIZE; i++)
ans[i] = x[i] & a[i];
return ans;
}

Bitset Bitset::operator | (const Bitset &x) const
{
Bitset ans;
for (int i = 0; i< SIZE; i++)
ans[i] = x[i] | a[i];
return ans;
}

Bitset Bitset::operator ^ (const Bitset &x) const
{
Bitset ans;
for (int i = 0; i < SIZE; i++)
ans[i] = x[i] ^ a[i];
return ans;
}

Bitset Bitset::operator << (int x) const
{
Bitset ans;
int y = x >> 5, z = x & 31;
if (z)
for (int i = SIZE - 1; i >= y + 1; i--)
ans[i] = (a[i - y] << z) | (a[i - y - 1] >> (BIT - z));
else
for (int i = SIZE - 1; i >= y + 1; i--)
ans[i] = a[i - y];
ans[y] = a[0] << z;
return ans;
}

Bitset Bitset::operator >> (int x) const
{
Bitset ans;
int y = x >> 5, z = x & 31;
if(z)
for (int i = 0; i < SIZE - y - 1; i++)
ans[i] = (a[i + y] >> z) | (a[i + y + 1] << (BIT - z));
else
for (int i = 0; i < SIZE - y - 1; i++)
ans[i] = a[i + y];
ans[SIZE - y - 1] = a[SIZE - 1] >> z;
return ans;
}

Bitset & Bitset::operator &= (const Bitset &x)
{
for (int i = 0; i < SIZE; i++)
a[i] &= x[i];
return *this;
}

Bitset & Bitset::operator |= (const Bitset &x)
{
for (int i = 0; i < SIZE; i++)
a[i] |= x[i];
return *this;
}

Bitset & Bitset::operator ^= (const Bitset &x)
{
for (int i = 0; i < SIZE; i++)
a[i] ^= x[i];
return *this;
}

Bitset & Bitset::operator <<= (int x)
{
int y = x >> 5, z = x & 31;
if(z)
for (int i = SIZE - 1; i >= y + 1; i--)
a[i] = (a[i - y] << z) | (a[i - y - 1] >> (BIT - z));
else
for (int i = SIZE - 1; i >= y + 1; i--)
a[i] = a[i - y];
a[y] = a[0] << z;
for (int i = 0; i < y; i++)
a[i] = 0;
return *this;
}

Bitset & Bitset::operator >>= (int x)
{
int y = x >> 5, z = x & 31;
if(z)
for (int i = 0; i < SIZE - y - 1; i++)
a[i] = (a[i + y] >> z) | (a[i + y + 1] << (BIT - z));
else
for (int i = 0; i < SIZE - y - 1; i++)
a[i] = a[i + y];
a[SIZE - y - 1] = a[SIZE - 1] >> z;
for (int i = SIZE - y; i < SIZE; i++) a[i] = 0;
return *this;
}

Bitset Bitset::operator ~ () const
{
Bitset ans;
for (int i = 0; i < SIZE; i++)
ans[i] = ~a[i];
return ans;
}

Bitset Bitset::Reverse(int x) const
{
Bitset ans;
int y = x >> 5, z = x & 31;
for (int i = 0; i <= y; i++)
ans[i] = BitReverse(a[y - i]);
return ans >> (BIT - z);
}

void Bitset::Set(int x, bool f)
{
int y = x >> 5, z = x & 31;
a[y] |= 1 << z;
if (!f)
a[y] ^= 1 << z;
}

void Bitset::Clear()
{
memset(a, 0, sizeof a);
}

int Bitset::AllBit() const
{
int ans = 0;
for (int i= 0; i < SIZE; i++)
ans += BitNum(a[i]);
return ans;
}

bool Bitset::CheckBit(int x) const
{
int y = x >> 5, z = x & 31;
return a[y] >> z & 1;
}

void Bitset::Flip(int x)
{
int y = x >> 5, z = x & 31;
a[y] ^= 1 << z;
}

效果

POJ2443是一道bitset模板题,我们不如来试试我们的bitset。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<iostream>
using namespace std;
typedef long long ll;
#ifndef ONLINE_JUDGE
#define dbg(x...) do{cout << "\033[33;1m" << #x << "->" ; err(x);} while (0)
void err(){cout << "\033[39;0m" << endl;}
template<template<typename...> class T, typename t, typename... A>
void err(T<t> a, A... x){for (auto v: a) cout << v << ' '; err(x...);}
template<typename T, typename... A>
void err(T a, A... x){cout << a << ' '; err(x...);}
#else
#define dbg(...)
#endif
#define inf 1ll << 50
const int SN = 1005;
const int N = 5e5 + 5;

unsigned int BitReverse(unsigned int x)
{
static unsigned int a[1 << 16 | 1], now = 1;
if(now) {
a[0] = now = 0;
for (int i = 1; i < 1 << 16; i++)
a[i] = (a[i >> 1] >> 1) | ((i & 1) << 15);
}
return a[x >> 16] | (a[x & 65535] << 16);
}

int BitNum(unsigned int x)
{
static unsigned int a[1 << 16 | 1], now = 1;
if (now)
{
a[0] = now = 0;
for (int i = 1; i < 1 << 16; i++)
a[i] = a[i >> 1] + (i & 1);
}
return a[x >> 16] + a[x & 65535];
}

struct Bitset {
static const int BIT = 32;
static const int SIZE = SN / BIT + 1;
unsigned int a[SIZE];

Bitset();

unsigned int & operator [] (const int);
const unsigned int & operator [] (const int) const;

Bitset operator & (const Bitset &) const;
Bitset operator | (const Bitset &) const;
Bitset operator ^ (const Bitset &) const;
Bitset operator << (int) const;
Bitset operator >> (int) const;

Bitset & operator &= (const Bitset &);
Bitset & operator |= (const Bitset &);
Bitset & operator ^= (const Bitset &);
Bitset & operator <<= (int);
Bitset & operator >>= (int);

Bitset operator ~ () const;

// reverse the binary bit in [0, x)
// "000110010".Reverse(5) = "000001001"
Bitset Reverse(int x) const ;

// set the xth binary bit value to f
void Set(int x, bool f = true);

// set all binary bit value to 0
void Clear();

// return the xth binary bit value
bool CheckBit(int x) const ;

// return the number of one in binary bit value
int AllBit() const;

void Flip(int x);
}s[N];

Bitset::Bitset()
{
memset(a, 0, sizeof a);
}

unsigned int & Bitset::operator [] (const int x)
{
return a[x];
}

const unsigned int & Bitset::operator [] (const int x) const
{
return a[x];
}

Bitset Bitset::operator & (const Bitset &x) const
{
Bitset ans;
for (int i = 0; i < SIZE; i++)
ans[i] = x[i] & a[i];
return ans;
}

Bitset Bitset::operator | (const Bitset &x) const
{
Bitset ans;
for (int i = 0; i< SIZE; i++)
ans[i] = x[i] | a[i];
return ans;
}

Bitset Bitset::operator ^ (const Bitset &x) const
{
Bitset ans;
for (int i = 0; i < SIZE; i++)
ans[i] = x[i] ^ a[i];
return ans;
}

Bitset Bitset::operator << (int x) const
{
Bitset ans;
int y = x >> 5, z = x & 31;
if (z)
for (int i = SIZE - 1; i >= y + 1; i--)
ans[i] = (a[i - y] << z) | (a[i - y - 1] >> (BIT - z));
else
for (int i = SIZE - 1; i >= y + 1; i--)
ans[i] = a[i - y];
ans[y] = a[0] << z;
return ans;
}

Bitset Bitset::operator >> (int x) const
{
Bitset ans;
int y = x >> 5, z = x & 31;
if(z)
for (int i = 0; i < SIZE - y - 1; i++)
ans[i] = (a[i + y] >> z) | (a[i + y + 1] << (BIT - z));
else
for (int i = 0; i < SIZE - y - 1; i++)
ans[i] = a[i + y];
ans[SIZE - y - 1] = a[SIZE - 1] >> z;
return ans;
}

Bitset & Bitset::operator &= (const Bitset &x)
{
for (int i = 0; i < SIZE; i++)
a[i] &= x[i];
return *this;
}

Bitset & Bitset::operator |= (const Bitset &x)
{
for (int i = 0; i < SIZE; i++)
a[i] |= x[i];
return *this;
}

Bitset & Bitset::operator ^= (const Bitset &x)
{
for (int i = 0; i < SIZE; i++)
a[i] ^= x[i];
return *this;
}

Bitset & Bitset::operator <<= (int x)
{
int y = x >> 5, z = x & 31;
if(z)
for (int i = SIZE - 1; i >= y + 1; i--)
a[i] = (a[i - y] << z) | (a[i - y - 1] >> (BIT - z));
else
for (int i = SIZE - 1; i >= y + 1; i--)
a[i] = a[i - y];
a[y] = a[0] << z;
for (int i = 0; i < y; i++)
a[i] = 0;
return *this;
}

Bitset & Bitset::operator >>= (int x)
{
int y = x >> 5, z = x & 31;
if(z)
for (int i = 0; i < SIZE - y - 1; i++)
a[i] = (a[i + y] >> z) | (a[i + y + 1] << (BIT - z));
else
for (int i = 0; i < SIZE - y - 1; i++)
a[i] = a[i + y];
a[SIZE - y - 1] = a[SIZE - 1] >> z;
for (int i = SIZE - y; i < SIZE; i++) a[i] = 0;
return *this;
}

Bitset Bitset::operator ~ () const
{
Bitset ans;
for (int i = 0; i < SIZE; i++)
ans[i] = ~a[i];
return ans;
}

Bitset Bitset::Reverse(int x) const
{
Bitset ans;
int y = x >> 5, z = x & 31;
for (int i = 0; i <= y; i++)
ans[i] = BitReverse(a[y - i]);
return ans >> (BIT - z);
}

void Bitset::Set(int x, bool f)
{
int y = x >> 5, z = x & 31;
a[y] |= 1 << z;
if (!f)
a[y] ^= 1 << z;
}

void Bitset::Clear()
{
memset(a, 0, sizeof a);
}

int Bitset::AllBit() const
{
int ans = 0;
for (int i= 0; i < SIZE; i++)
ans += BitNum(a[i]);
return ans;
}

bool Bitset::CheckBit(int x) const
{
int y = x >> 5, z = x & 31;
return a[y] >> z & 1;
}

void Bitset::Flip(int x)
{
int y = x >> 5, z = x & 31;
a[y] ^= 1 << z;
}
//bitset

int main()
{
int n;
while (scanf("%d", &n) != EOF)
{
for (int i = 1; i <= n; i++)
s[i].Clear();
for (int i = 1; i <= n; i++)
{
int k;
scanf("%d", &k);
while (k--)
{
int a;
scanf("%d", &a);
s[a].Set(i);
}
}
int m;
scanf("%d", &m);
while (m--)
{
int a, b;
scanf("%d%d", &a, &b);
if ((s[a] & s[b]).AllBit())
puts("Yes");
else
puts("No");
}
}
return 0;
}

这份代码测下来结果是没啥问题,但是速度上和stl原生的bitset相比并没有快很多,大约只有100ms的差距,可能还需要改进。

其他优化

其实我们还可以用longlong来压缩,比原来压32位多压一倍。

0%