bitset 位容器集合

来源:互联网 发布:cpu风扇转速软件 编辑:程序博客网 时间:2024/05/16 13:04

bitset

 bitset容器是一个bit位元素的序列容器,每个元素只占一个bit位,取值为0或1,因而很节省内存空间。它的10个元素只用了两个字节的空间。

头文件:#include<bitset>

bitset类的方法列表:(bitset<N>b)

b.any() :  b中是否存在置为1的二进制位?

b.none():  b中不存在置为1的二进制位吗? 

b.count():  b中置为1的二进制位的个数

b.size() :  b中二进制位的个数

b[pos]:  访问b中在pos处的二进制位

b.test(pos):  b中在pos处的二进制是否为1?

b.set():  把b中所有二进制位都置为1

b.reset():  把b中所有二进制位都置为0

b.reset(pos):  把b中在pos位的二进制置为0

b.flip():  把b中所有二进制位逐位取反

b.flip(pos):  把b中在pos位的二进制位取反

b.to_ulong():  用b中同样的二进制位返回一个unsigned long 值

os<<b:  把b中的位集输出到os流

b<<k:  把b中所有为1的位向高位移k个位置

b>>k:  把b中所有为1的位向低位移k个,自动舍弃超出范围的位

b=(int)n: 用n的二进制位填充b

两个同级bitset对象还可以进行^、&、| 等操作。

bitset的常数很小,进行整体操作的复杂度大概b.size()/64的样子。


1. 创建bitset对象

创建bitset对象时必须要指定容器大小。bitset对象的大小一经定义就不能修改了。

bitset<100>b:定义bitset对象b,能容纳100个比特位(0-99),所有位初始都为0。


2.设置元素值

(1)采用下标法:b[pos]=1。

bitset<5>b;

b[1]=1,b[2]=1,b[4]=1;

输出:01101


(2)采用set()方法,一次性将元素设置为1。

bitset<5>b;

b.set();

输出:11111


(3)采用set(pos,1)方法直接将pos位置为1或者0

bitset<5>b;

b.set(1,1);

b.set(2,1);

b.set(4,1);

输出:01101


(4)采用reset(pos)方法将pos位置为0

bitset<5>b;

b.set(1,1);

b.set(2,1);

b.set(4,1);

b.reset(2);

输出:01001


3.输出元素

(1)采用下标法输出元素

bitset<5>b;

for(int i=0;i<5;i++) cout<<b[i];

for(int i=0;i<b.size();i++) cout<<b[i];


(2)直接向输出流输出所有元素

cout<<b<<endl;



bitset在很多优化技巧上可以用到,是很好用的stl容器。

我自己拉了一个bitset专题练习,对相关知识进行总结一下:

 bitset 练习


HDU - 2051 

Bitset

 

题意:输出n个二进制位。

bitset裸题,直接采用上述赋值法,然后从首个不为0的高位到低位输出即可。

 int n;    bitset<11>b;    while(~scanf("%d",&n))    {        b=n;        int flag=0;        for(int i=10;i>=0;i--)        {            if(b[i]) printf("%d",int(b[i])),flag=1;           else if(flag) printf("%d",int(b[i]));        }        puts("");    }


HDU - 4920  

Matrix multiplication

 

题意:给你两个n*n的矩阵,求矩阵乘积取余3的矩阵。n可达800,时限2s。

直接800^3复杂度5.12e8超时的风险。注意结果对3取余再输出,那么每个初始矩阵的值都只会是0、1、2中的一个。我们可以用3维的bitset表示每一个(i,j)是1还是2,0对大答案无贡献。这样我们普通矩阵相乘是n^3的复杂度,但第三层循环完全可以用bitset优化,对应行和对应列进行&操作再统计有多有个1即知道答案的贡献了。复杂度n^3/64。

int a[maxn][maxn],b[maxn][maxn],c[maxn][maxn];bitset<maxn>aa[maxn][2],bb[maxn][2];int main(){    int n;    while(~scanf("%d",&n))    {        for(int i=0; i<n; i++)            for(int j=0; j<n; j++)            {                aa[i][0].reset();                aa[i][1].reset();                bb[i][0].reset();                bb[i][1].reset();            }        for(int i=0; i<n; i++)            for(int j=0; j<n; j++)            {                scanf("%d",&a[i][j]);                a[i][j]%=3;                if(a[i][j]==1) aa[i][0][j]=1;                else if(a[i][j]==2) aa[i][1][j]=1;            }        for(int i=0; i<n; i++)            for(int j=0; j<n; j++)            {                scanf("%d",&b[i][j]);                b[i][j]%=3;                if(b[i][j]==1) bb[j][0][i]=1;                else if(b[i][j]==2) bb[j][1][i]=1;            }//            for(int i=0;i<n;i++)//                for(int j=0;j<n;j++)//                printf("i=%d j=%d aa0=%d bb0=%d aa1=%d bb1=%d\n",i,j,int(aa[i][0][j]),int(bb[j][0][i]),int(aa[i][1][j]),int(bb[j][1][i]));        for(int i=0; i<n; i++)            for(int j=0; j<n; j++)                c[i][j]=(2*(aa[i][1]&bb[j][0]).count()+2*(aa[i][0]&bb[j][1]).count()+(aa[i][0]&bb[j][0]).count()+4*(aa[i][1]&bb[j][1]).count())%3;        for(int i=0; i<n; i++)            for(int j=0; j<n; j++)                printf("%d%c",c[i][j],j==n-1?'\n':' ');    }    return 0;}


POJ - 2443 

Set Operation

 

题意:有n个集合,每个集合k个数,Q次查询,每次查询是否存在一个集合同时包含查询的两个数。

每一位表示一个集合,这一位对应的数在哪个集合中出现过就将对应位置为,查询两个数只需将这个两个数位对应的集合进行&后统计1的个数是否为0即可。

const int N=1e4+5;const int maxn=800+5;bitset<1001>b[N];int main(){    int n,q,k,p;    while(~scanf("%d",&n))    {        for(int i=0;i<N;i++) b[i].reset();        for(int i=0;i<n;i++)        {            scanf("%d",&k);            while(k--)            {                scanf("%d",&p);                b[p][i]=1;            }        }        scanf("%d",&q);        while(q--)        {           scanf("%d%d",&k,&p);           if((b[k]&b[p]).count()) puts("Yes");           else puts("No");        }    }    return 0;}


HDU - 5036  

Explosion

 

题意:魔塔都玩过吧,每个房间里都有一些钥匙,能打开对应的门,当没有钥匙能开门后只能用一颗炸弹炸开一扇门,求打开所有门需要炸弹数量的期望。

期望等于值乘以概率,值当然为1了,概率即能够通往这个门的数量的倒数。累加求和就是ans。那就得求出打开哪些门才能打开这扇门,这就是一个传递闭包的过程,用bitset优化

const int N=1e3+5;const int maxn=800+5;bitset<N>b[N];int t,n,k,p;void init(){    scanf("%d",&n);    for(int i=1; i<=n; i++) b[i].reset();    for(int i=1; i<=n; i++)    {        scanf("%d",&k);        while(k--)        {            scanf("%d",&p);            b[i][p]=1;        }        b[i][i]=1;    }    for(int i=1; i<=n; i++)        for(int j=1; j<=n; j++)            if(b[j][i]) b[j]|=b[i];}int main(){    scanf("%d",&t);    int t1=t;    while(t--)    {        init();        double ans=0.0;        for(int i=1; i<=n; i++)        {            double num=0;            for(int j=1; j<=n; j++)                if(b[j][i]) num+=1;            ans+=1.0/num;        }        printf("Case #%d: %.5f\n",t1-t,ans);    }    return 0;}



HDU - 5313  

Bipartite Graph

 

题意:给你n个点,m条边,求最多能加多少边使得这n个点变成一个完全二分图。

什么是完全二分图呢,就是两个集合中的点都互相连边了。先来看一个不等式:ab<=((a+b)^2)/2当且仅当a==b取等。于是我们可以二分图染色将原有边分成的两部分求出来,剩下的就相当于选一些集合合并,合并后的集合的点数和剩下的点数的差值最小。可以用背包优化,但bitset会更优一点,代码写好写好理解。如果用背包,那么背包容量应该设为n/2。但bitset可以直接左移或者右移表示所有数同时加上或减去一个数,我们求出所有组合然后枚举最优解即可。具体怎么求出所有组合看代码:

这个优化可以改变很多题的,很多01背包就可以用这个优化解:比如NYOJ:邮票分你一半、zb的生日。

const int N=1e4+10;bitset<N>cur;struct Edge{    int to,next;} e[N*20];struct Node{    int a,b;} node[N];int head[N],tot,flag[N];void init(){    tot=0;    memset(flag,0,sizeof(flag));    memset(head,-1,sizeof(head));    cur.reset();    cur.set(0);}void add(int u,int v){    e[tot].to=v,e[tot].next=head[u];    head[u]=tot++;    e[tot].to=u,e[tot].next=head[v];    head[v]=tot++;}void dfs(int &a,int &b, int u,int f){    flag[u]=1;    if(f) a++;    else b++;    for(int i=head[u]; i+1; i=e[i].next)    {        int v=e[i].to;        if(flag[v]) continue;        dfs(a,b,v,f^1);    }}int main(){    int t,n,m;    scanf("%d",&t);    while(t--)    {        init();        scanf("%d%d",&n,&m);        int u,v,k=0;        for(int i=0;i<m;i++)        {            scanf("%d%d",&u,&v);            add(u,v);        }        for(int i=1; i<=n; i++)            if(!flag[i])            {                int a=0,b=0;                dfs(a,b,i,1);                node[k].a=a,node[k++].b=b;            }        for(int i=0; i<k; i++)            cur=(cur<<node[i].a)|(cur<<node[i].b);        int ans=0;        for(int i=1; i<=n; i++)            if(cur[i])                ans=max(ans,(n-i)*i-m);        printf("%d\n",ans);    }    return 0;}

 HDU - 6085 

Rikka with Candies

 

今年多校的新题,正解应该是手写压位,但优化好的bitset也可以卡过,题意和思路就不详细介绍了。



原创粉丝点击