bitset及压位运算
题面
有N个集合和Q个询问,每个询问给定两个整数x,y,问x,y是否同时属于某个集合。
输入
输入一个整数N(1 <= N <= 1000),表示集合的数目。
接下来N行,每行先输入一个整数C(1 <= C <= 10000)表示某个集合的大小。
然后输入C个整数表示该集合的所有元素(元素是1到10000之间的整数)。
接下来输入一个整数Q(1 <= Q <= 200000),表示询问的数目。
接下来Q行,每行两个整数x y (1 <= x, y <= 10000)。
输出
对于每一个询问,判断x 与 y是否同时属于某一个集合,
如果是输出Yes,否则输出No。
样例
输入
复制3 3 1 2 3 3 1 2 5 1 10 4 1 3 1 5 3 5 1 10输出
复制Yes Yes No No提示
子任务1,20分,1 \le N \le 1001≤N≤100,1 \le Q \le 1001≤Q≤100,1 \le C \le 1001≤C≤100。
子任务2,30分,元素是1到10之间的整数。
子任务3,50分,全范围。
分析
原本第一反应用并查集做...但是仔细一看发现一个点可以在多个集合里,没法维护啊!
数据范围有限制,如果就是常规标记vis[C][N]显然会爆空间,所以引入压位运算
一个int去掉符号位是31位,而unsigned int没有符号位,所以可以用unsigned int
总共有1000个集合,假设一个整数每一位代表一个集合,那么用(1000/32)个整数就可以表示所有集合
如果把这大约40个整数写成二进制并且排列成矩阵
最初:
000000000....(每一行有32位数)
000000000....
.......(共有约40行)
000000000....
那么 i / 32表示在第几行,i % 32表示在第几列,用 | 运算更新
查询的时候用 & 运算判断查询
( “ | ”运算只要有一个为1即结果为1,“ & ”运算要两个都为1结果才为1,“更新”表示更新这个数在某个集合里,“判断”表示判断两个数都在某个集合里)
代码
#include<bits/stdc++.h> using namespace std; const int N = 1005; unsigned int vis[10005][42]; //压位运算 int n,c,q; int main() { scanf("%d",&n); for(int i=1;i<=n;i++) { int c; scanf("%d",&c); for(int j=1;j<=c;j++) { int x; scanf("%d",&x); vis[x][i/32]|=1<<(i%32); } } scanf("%d",&q); for(int i=1;i<=q;i++) { int x,y; bool flag=0; scanf("%d%d",&x,&y); for(int i=0;i<=40;i++)//0~39,注意从0开始 { if(vis[x][i]&vis[y][i]) { flag=1; printf("Yes\n"); break; } } if(!flag) printf("No\n"); } return 0; }
关于bitset
bitset本质貌似就是压位(上述操作也可以用bitset实现,但是我不太了解)
这里推荐一篇博客---传送门
update(2021.10.5),看到了同机房大佬wind_whisper的博客,简洁易懂,挂上传送门:STL:bitset用法详解