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 1001N100,1 \le Q \le 1001Q100,1 \le C \le 1001C100。

子任务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用法详解

qwq

posted @ 2021-05-05 10:55  conprour  阅读(264)  评论(1编辑  收藏  举报