位向量:bitset
bitset
是一个多位二进制数,如同状态压缩的二进制数。使用bitset
时,需要引入头文件#include <bitset>
。bitset<1000> s
表示定义一个1000位的二进制数s
。
基本的位运算~(取反)、&(与)、|(或)、^(异或)、>>(右移)、<<(左移)、==(相等比较)、!=(不相等比较)。
可以通过“[]
”操作符直接得到第k
位的值,也可以通过赋值操作改变该位的值。例如s[k] = 1
,表示将二进制数s
的第k
位,置1。需要注意的是,最右侧为低位第0位,左侧为高位。1000位的二进制数,位序自右向左是0~999。
count()
:统计有多少位是1。any()
:若至少有一位是1,则返回true
。none()
:若没有为是1,全为0,则返回true
。set()
:将所有位,置1。set(k)
:将第k
位,置1。set(k,val)
:将第k
位的值改为val
,即s[k] = val
。reset()
:将所有位,置0。reset(k)
:将第k
位置0,即s[k] = 0
。flip()
:将所有位取反。flip(k)
:将第k
位取反。size()
:返回大小(位数)。to_ulong()
:返回它转换为unsigned long
的结果,如果超出范围,则报错。to_string()
:返回它转换为string
的结果。
bitset定义和初始化
bitset
的构造函数
bitset<n> b;//b有n位,每位都为0
bitset<n> b(u);//b是unsigned long型u的一个副本
bitset<n> b(s);//b是string对象s中含有的位串副本
bitset<n> b(s, pos, n);//b是s中从位置pos开始的n位副本
在定义bitset
时,要明确bitset
有多少位,必须在尖括号内给出它的长度值,给出的长度值必须是常量表达式。bitset<32> bitvec
表示定义bitvec
为32位的bitset
对象,bitvec
的位序自右向左为0~31。
用string对象初始化bitset对象
当用string
对象初始化bitset
对象时,string
对象直接被表示为位模式。从string
对象读入位集的顺序是从右向左。
string strval("1100");
bitset<32> bitv(strval);
在bitv
的位模式中,第2,3位被置为1,其余位置都被置为0。如果string
对象的字符个数小于bitset
类型的长度,则高阶位将被置为0。
注意:string
对象和bitset
对象之间是反向转化的:string
对象的最右边字符(即下标最大的字符)用来初始化bitset
对象的低阶位(即下标为0的位)。
也可以用某个子串:
string str("110000100010");
bitset<32> bitv(str, 5, 4);//从str[5]开始取4位,即0001
bitset<32> bitvs(str, str.size()-4);//取末尾4位,即0010
bitv(str, 5, 4)
表示从str[5]
开始取4个字符初始化bitv
。如果省略第3个参数,则表示取从开始位置一直到string
末尾的所有字符。bitvs(str, str.size()-4)
表示取出str
末尾的4位来对bitvs
的低4位进行初始化操作。
bitset上的操作
any/none
如果在对象中有一个或多个二进制位被置为1,则any
操作返回true
,否则返回false
;相反,如果bitset
对象中的二进制位全为0,则none
操作返回true
。
bitset<32> bitvec;//32位,将所有位都置为0
bool is_set = bitvec.any();//所有位为0,返回false
bool is_not_set = bitvec.none();//所有位为0,返回true
count/size
可以使用count
操作统计二进制位为1的个数:
size_t bits_set = bitvec.count();
count
操作的返回类型是标准库中命名为size_t
的类型,与vector
和string
中的size
操作一样,bitset
的size
操作返回bitset
对象中二进制位的个数,返回值的类型是size_t
。
size_t bits_set = bitvec.size()
set/test
用下标操作符或写某个索引位置的二进制位。
for (int index = 0; index != 32;index+=2){//把bitvec中的偶数下标的位都置为1
bitvec[index] = 1;
}
除了用下标操作符,还可以用set
设置给定二进制位的值。
for (int index = 0; index != 32;index+=2){//把bitvec中的偶数下标的位都置为1
bitvec.set(index);
}
为测试某个二进制位是否为1,可以用test
操作或者下标操作符。如果测试的二进制位为1,则返回true
,否则返回false
。
if(bitvec.test(i)){
;
}
if(bitvec[i]){
;
}
set/reset
set
和reset
操作分别用来对整个bitset
对象的所有二进制位都置1和都置0。
bitvec.set();//都置为1
bitvec.reset();//都置为0
flip
flip
操作可以对bitset
对象的所有位或特定位按位取反。
bitvec.flip(0);//0位取反
bitvec[0].flip();//0位取反
bitvec.flip();//所有位取反
十进制转换二进制
cout << bitset<x>(y);//输出y转化为二进制后的数,共x位,不足补0,高位舍去
cout << bitset<5>(12) << endl;//输出01100
题目描述
给定
N
N
N个集合,第
i
i
i个集合
S
i
S_i
Si有
C
i
C_i
Ci个元素(集合可以包含两个相同的元素)。集合中的每个元素都用1~10000的正数表示。查询两个给定元素i和j是否同时属于至少一个集合。换句话说,确定是否存在一个数字
k
(
1
≤
k
≤
N
)
k(1 \leq k \leq N)
k(1≤k≤N),使得元素
i
i
i和元素
j
j
j都属于
S
k
S_k
Sk。
输入:输入的第1行包含一个整数
N
(
1
≤
N
≤
1000
)
N(1 \leq N \leq 1000)
N(1≤N≤1000),表示集合的数量。第
2
∼
N
+
1
2 \sim N+1
2∼N+1行,每行都以数字
C
i
(
1
≤
C
i
≤
10000
)
C_i(1 \leq C_i \leq 10000)
Ci(1≤Ci≤10000)开始,后面有
C
i
C_i
Ci个数字,表示该集合中的元素。第
N
+
2
N+2
N+2行包含一个数字
Q
(
1
≤
Q
≤
200000
)
Q(1 \leq Q \leq 200000)
Q(1≤Q≤200000),表示查询数。接下来的
Q
Q
Q行,每行都包含一对数字
i
i
i和
j
j
j
(
1
≤
j
,
j
≤
10000
(1 \leq j,j \leq 10000
(1≤j,j≤10000,
i
i
i可以等于
j
j
j),表示待查询的元素。
输出:对于每个查询,如果存在这样的数字
k
k
k,则输出
Y
e
s
Yes
Yes,否则输出
N
o
No
No。
算法设计
查询两个元素是否同属于一个集合(至少一个)。所属集合可以用二进制表示法。
输入样例:
3 //表示3个集合
3 1 2 3 //表示第一个集合包含3个元素1、2、3
3 1 2 5 //表示第二个集合包含3个元素1、2、5
1 10 //表示第三个集合包含1个元素10
每个元素都可以用一个二进制数记录所属的集合。最右侧为低位0位,自右向左。例如1属于第1个集合,就将1对应的二进制数的第1位置为1,即s[1] = 0010;1还属于第2个集合,就将1对应的二进制数的第2位置为1,即s[1] = 0110;s[1] = 0110表示元素1属于1、2两个集合。同理,s[2] = 0110,s[3] = 0010,s[5] = 0100,s[10] = 1000。
4 //表示查询数
1 3 //表示查询1和3 是否属于同一集合,计算s[1] & s[3] = 0110 & 0010,统计1的个数,即1和3同属于集合的个数,输出“Yes”
1 5 //s[1] & s[5] = 0110 & 0100 = 0100,统计1的个数,输出“Yes”
3 5 //s[3] & s[5] = 0010 & 0100 = 0000,统计1的个数,输出“No”
1 10 //s[1] & s[10] = 0110 & 1000 = 0000,统计1的个数,输出“No”
bitset解决
- 定义一个
bitset
数组,对每个数都用二进制表示。 - 根据输入数据,将原数所属集合对应的位置为1。
- 根据查询输入的两个数x,y,统计s[x] & s[y]运算后二进制数中1的个数,如果大于或等于1,则输出Yes,否则输出No
#include <iostream>
#include <bitset>
#include <string>
#include <memory>
using namespace std;
int main()
{
int N,Q,num,x,y;
scanf("%d",&N);
unique_ptr<bitset<1010>[]> s(new bitset<1010>[N+1]);//s[x]表示元素x所属集合的二进制表示
for (int i = 1; i <= N; ++i) {
scanf("%d",&num);
while(num--){
scanf("%d",&x);
s[x][i] = 1;
}
}
scanf("%d",&Q);
while(Q--){
scanf("%d %d",&x,&y);
if((s[x]&s[y]).count()){//统计与运算后二进制数中1的个数
printf("Yes\n");
}
else{
printf("No\n");
}
}
return 0;
}
输入:
3
3 1 2 3
3 1 2 5
1 10
4
1 3
1 5
3 5
1 10
输出:
Yes
Yes
No
No