【算法竞赛学习笔记】Bitset详解和应用

本文介绍了位集(bitset)数据结构,包括其特性、声明方式、常用方法以及在ACM竞赛和数据结构问题中的优化应用,如传递闭包、动态规划转移、区间操作等。位集因其支持位运算、节省空间和优化时间而成为解决特定问题的利器。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


title : bitset
date : 2021-8-20
tags : ACM,数据结构
author : Linno


Bitset

bitset是一种类似数组的数据结构,它的每一个元素只能是0或1,每个元素只用1bit的空间。可以使用只包含‘0’或‘1’的字符串构造。

优点

支持所有的位运算。

空间占用非常小,也可用于优化时间。

声明方式

#include<bitset>
bitset<30>bi;

string s="100101";
bitset<10>bs(s); //长度为10,前面用0补充
cout<<bs<<endl; //0000100101
cout<<bs[0]<<endl; //下标是从右到左的,可以像数组一样访问每一位

方法

bi.size() //返回大小,即位数
bi.count() //返回1的个数
bi.any() //返回是否有1
bi.none() //返回是否没有1
bi.set() //全部变成1
bi.set(p)//将第p+1位变成1
bi.set(p,x)//将p+1位变成x
bi.reset() //全部变成0
bi.reset(p) //将p+1位变成0
bi.flip() //全部取反
bi.flip(p) //将p+1位取反
bi.test(p) //返回i的索引,如果不存在则返回0
bi.to_ulong() //返回它转换为unsigned long的结果,如果超出范围则报错
bi.to_ullong() //返回它转换为unsigned long long的结果
bi.to_string() //返回它转换位string的结果

bitset优化传递闭包

[JSOI2010]连通数
#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define int long long
using namespace std;
const int maxn=2007;

bitset<maxn>bi[maxn];
int n,ans=0;
string str;

signed main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>str;
		for(int j=1;j<=n;j++)
			bi[i][j]=(str[j-1]=='1'||i==j)?1:0;
	}
	for(int i=1;i<=n;i++)
		for(int j=1;j<=n;j++)
			if(bi[j].test(i)) bi[j]|=bi[i];
	for(int i=1;i<=n;i++) ans+=bi[i].count();
	cout<<ans<<endl;
	return 0;
}

bitset优化Dp转移

转移式dp[i][k]∣=dp[i−1][k−j∗j]dp[i][k]|=dp[i-1][k-j*j]dp[i][k]=dp[i1][kjj]

#include<bits/stdc++.h>
#define inf 0x3f3f3f3f
#define int long long
using namespace std;
const int N=105;
bitset<N*N*N>dp[N];
int n,ans=0;
int l[N],r[N];

signed main(){
	cin>>n;
	for(int i=1;i<=n;i++){
		cin>>l[i]>>r[i];
	}
	dp[0].set(0);
	for(int i=1;i<=n;i++){
		for(int j=l[i];j<=r[i];j++){
			dp[i]|=dp[i-1]<<(j*j);
		}
	}
	cout<<dp[n].count()<<"\n";
	return 0;
}
luoguP1537 弹珠

01背包+bitset优化,效率非常优秀。

题意是:有1~6大小的若干个弹珠,要求能否平分总数。

#include<bits/stdc++.h>
using namespace std;
const int mod=1e9+7;

int read(){	int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-') f=f*-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;}

int sum,s[10],idx=0;
bitset<20010>frog;

void clear(){
	sum=0;
	frog.reset();
}

signed main(){
	while(1){
		clear();
		for(int i=1;i<=6;i++) s[i]=read();
		for(int i=1;i<=6;i++) sum+=s[i]*i; //记录总数
		if(!sum) break;
		frog.set(0);
		for(int i=1;i<=6;i++){
			for(int j=1;j<=s[i];j++) frog|=(frog<<i);
		}
		printf("Collection #%d:\n",++idx);
		if(frog.test(sum>>1)&&sum%2==0) puts("Can be divided.\n");
		else puts("Can't be divided.\n");
	}
	return 0;
}

luoguP5020 [NOIP2018 提高组] 货币系统

bitset优化完全背包

题意:给定货币系统(n,a),求等价的货币系统(m,b),令m最小(等价说明他们能表示的面额一样)

#include<bits/stdc++.h>
using namespace std;

int read(){	int x=0,f=1;char ch=getchar();while(ch<'0'||ch>'9'){if(ch=='-') f=f*-1;ch=getchar();}while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}return x*f;}

int t,n;
int a[105];
bitset<25005>s; 

signed main(){
	t=read();
	while(t--){
		s.reset();
		n=read();
		for(int i=1;i<=n;i++) a[i]=read();
		sort(a+1,a+1+n);
		s[0]=1;
		int ans=0;
		for(int i=1;i<=n;i++){ //只要用最小的数表示a就可以了
			if(!s[a[i]]){
				++ans;
				int x=a[i];
				while(x<=a[n]){ //第i个面额可以表示出的数额
					s|=s<<x;
					x<<=1;
				}
			}
		}
		printf("%d\n",ans);
	}
	return 0;
}

bitset优化区间操作

//luoguP3674 小清新人渣的本愿
#include<bits/stdc++.h>
using namespace std;
const int N=2e5+7;
const int mod=1e9+7;
typedef long long ll;

namespace IPT{
	const int L=1000000;
	char buf[L],*front=buf,*end=buf;
	char GetChar(){
		if(front==end){
			end=buf+fread(front=buf,1,L,stdin);
			if(front==end) return -1; 
		}
		return *(front++);
	}
}

template<typename T>
inline void qr(T &x){
	char ch=IPT::GetChar(),lst=' ';
	while(ch>'9'||ch<'0') lst=ch,ch=IPT::GetChar();
	while(ch>='0'&&ch<='9') x=(x<<1)+(x<<3)+(ch^48),ch=IPT::GetChar();
	if(lst=='-') x=-x;
}

template<typename T>
inline void ReadDb(T &x){
	char ch=IPT::GetChar(),lst=' ';
	while(ch>'9'||ch<'0') lst=ch,ch=IPT::GetChar();
	while(ch>='0'&&ch<='9') x=x*10+(ch^48),ch=IPT::GetChar();
	if(ch=='.'){
		ch=IPT::GetChar();
		double base=1;
		while(ch>='0'&&ch<='9') x+=(ch^48)*((base*=0.1)),ch=IPT::GetChar();
	}
	if(lst=='-') x=-x;
}

namespace OPT{
	char buf[120];
}

template<typename T>
inline void qw(T x,const char aft,const bool pt){
	if(x<0){x=-x,putchar('-');}
	int top=0;
	do{OPT::buf[++top]=x%10+'0';}while(x/=10);
	while(top) putchar(OPT::buf[top--]);
	if(pt) putchar(aft);
}

const int maxn=100010;

bitset<maxn>s1,s2; //第i为表示i这个数字有无出现,再建一个表示N-x是否出现 

int n,m;
int MU[maxn],bel[maxn],oc[maxn];

struct Ask{
	int opt,l,r,v,id;
	bool ans;
	inline bool operator <(const Ask &_others)const{
		if(bel[this->l]!=bel[_others.l]) return this->l < _others.l;
		if(bel[this->l]&1) return this->r < _others.r;
		return this->r > _others.r; 
	}
}ask[maxn];

inline bool cmp(const Ask &_a,const Ask &_b){
	return _a.id<_b.id;
}

inline void add(int x){
	x=MU[x]; if(!(oc[x]++)) s1[x]=s2[n-x]=1;
}

inline void dlt(int x){
	x=MU[x]; if(!(--oc[x])) s1[x]=s2[n-x]=0;
}

signed main(){
	qr(n);qr(m);
	for(int i=1;i<=n;i++) qr(MU[i]);
	for(int i=1;i<=m;i++){
		Ask &now=ask[i];
		qr(now.opt);qr(now.l);qr(now.r);qr(now.v);
		now.id=i;
	}
	for(int i=1,sn=sqrt(n);i<=n;i++) bel[i]=i/sn;
	sort(ask+1,ask+1+m);
	int L=ask[1].l,R=L-1;
	for(int i=1;i<=m;i++){
		int l=ask[i].l,r=ask[i].r;
		while(L<l) dlt(L++);
		while(L>l) add(--L);
		while(R<r) add(++R);
		while(R>r) dlt(R--);
		int op=ask[i].opt,x=ask[i].v;
		if(op==1) ask[i].ans=(s1&(s1<<x)).any();//y和y+x同时存在等于s和s<<x同时存在一个1 
		else if(op==2) ask[i].ans=(s1&(s2>>(n-x))).any(); //n-i是否出现,右移(n-x)位就是x-i是否出现 
		else{
			for(int j=1;j*j<=x;j++) if(!(x%j)){
				if(s1[j]&&s1[x/j]){
					ask[i].ans=1;
					break;
				}
			}
		}
	}
	sort(ask+1,ask+1+m,cmp);
	for(int i=1;i<=m;i++) puts(ask[i].ans?"hana":"bi");
	return 0;
}

手写Bitset

用于解决bitset的过度封装导致的bitset的一些操作不能实现,比如两个二进制数求lowbit等问题。

#define Fusu_Bitset

#ifdef Fusu_Bitset
#include <cstddef>				//size_t used
#include <cstring>

#include <string>
#include <algorithm>
#endif

namespace Fusu {


template <size_t _len>
struct Bitset {
#define rg register
#define ci const int
#define cl const long long
    typedef long long int ll;
    typedef unsigned long long int ull;
    const static int _BitNum = 64;
    const static int _Size = _len / _BitNum + ((_len % _BitNum) == 0 ? 0 : 1);
    ull _Ary[_Size];
    ull _upceil;
    const static ull _INF = (1ull << 63) - 1 + (1ull << 63);
    Bitset() {											//constructed function of std::string is left out because i dont know how to implement it
		memset(_Ary, 0, sizeof _Ary);
		int _firstsize = _len % _BitNum;
		for (rg int i = 0; i < _firstsize; ++i) _upceil |= 1ull << i;
		if (!_firstsize) _upceil = _INF;
	}
    
    void reset() {*this = Bitset();}
                                                        //operators
                                                        
    void rtmve(const int &_v) {
        for (rg int i = _Size - 1; i >= _v; --i) this->_Ary[i] = this->_Ary[i - _v];
        for (rg int i = _v - 1; ~i; --i) this->_Ary[i] = 0;
    }
    
    void lftmve(const int &_v) {
        for (rg int i = 0; (i + _v) < _Size; ++i) this->_Ary[i] = this->_Ary[i + _v];
        for (rg int i = _Size - _v; i < _Size; ++i) this->_Ary[i] = 0;
    }
    
    Bitset& operator<<=(int _v) {
        if (_v < 0) {
            *this >>= -_v;
            return *this;
        }
        this->lftmve(_v / _BitNum);
        _v %= _BitNum;
        ull _tp = 0, _Pos = _BitNum - _v;
        for (rg int  i = 1; i <= _v; ++i) _tp |= 1ull << (_BitNum - i);
        ull _Lstv = 0;
        for (rg int i = _Size - 1; ~i; --i) {
            ull _Tp_Lstv = (_Ary[i] & _tp) >> _Pos;
            _Ary[i] <<= _v;
            _Ary[i] |= _Lstv;
            _Lstv = _Tp_Lstv;
        }
        this->_Ary[0] &= _upceil;
        return *this;
    }
    
    Bitset& operator>>=(int _v) {
        if (_v < 0) {
            *this <<= -_v;
            return *this;
        }
        this->rtmve(_v / _BitNum);
        _v %= _BitNum;
        ull _tp = (1ull << _v)- 1;
        ull _Lstv = 0, __Pos = _BitNum - _v;
        for (rg int i = 0; i < _Size; ++i) {
            ull _Tp_Lstv = (_Ary[i] & _tp) << __Pos;
            _Ary[i] >>= _v;
            _Ary[i] |= _Lstv;
            _Lstv = _Tp_Lstv;
        }
        this->_Ary[0] &= _upceil;
        return *this;
    }
    
    Bitset operator&(const Bitset &_others) const {
        Bitset _ret;
        for (rg int i = _Size - 1; ~i; --i) {
            _ret._Ary[i] = this->_Ary[i] & _others._Ary[i];
        }
        return _ret;
    }
    
    Bitset operator|(const Bitset &_others) const {
        Bitset _ret;
        for (rg int i = _Size - 1; ~i; --i) {
            _ret._Ary[i] = this->_Ary[i] | _others._Ary[i];
        }
        return _ret;
    }
    
    Bitset operator^(const Bitset &_others) const {
        Bitset _ret;
        for (rg int i = _Size - 1; ~i; --i) {
            _ret._Ary[i] = this->_Ary[i] ^ _others._Ary[i];
        }
        return _ret;
    }
    
    Bitset operator~() const {
        Bitset _ret;
        for (rg int i = _Size - 1; ~i; --i) {
            _ret._Ary[i] = ~this->_Ary[i];
        }
        return _ret;
    }
    
    Bitset operator<<(const int &_v) const {
        Bitset x = *this;
        x <<= _v;
        return x;
    }

    Bitset operator>>(const int &_v) const {
        Bitset x = *this;
        x >>= _v;
        return x;
    }
    
                                                        //member functions
    inline void __GetPos(const ull &_pos, int &__Pos, int &_v) {
        __Pos = _Size - _pos / _BitNum - 1;
        _v = _pos % _BitNum;
    }
    
    void set() {
        for (rg int i = 0; i < _Size; ++i) this->_Ary[i] = _INF;
    }
    
    void set(const ull &_pos, const bool val = true) {
        int __Pos , _v;
        __GetPos(_pos,__Pos,_v);
        if (val) {
            this->_Ary[__Pos] |= (1ull << (_v));
        } else {
            this->_Ary[__Pos] |= (1ull << (_v));
            this->_Ary[__Pos] ^= (1ull << (_v));
        }
    }
    
    int test(const ull &_pos) {
        int __Pos , _v;
        __GetPos(_pos,__Pos,_v);
        return this->_Ary[__Pos] & (1ull << (_v)) ? 1 : 0;
    }
    
    bool any() {
        for (rg int i = _Size - 1; ~i; --i) if (this->_Ary[i]) return true;
        return false;
    }
    
    bool none() {
        return !this->any();
    }
    
    ull conut() {
        ull _cnt = 0;
        for (rg int i = _Size - 1; ~i; --i) _cnt += __builtin_popcount(this->_Ary[i]);			
        /*
            *if u cant used double_underlined functions, 
            *u can set a val to maintain the num of true
            *and change it in other operators which would change the num of true
        */
        return _cnt;
    }
    
    void flip() {
        *(this) = ~(*this);
    }
    
    void flip(const ull &_pos) {
        if(this->test(_pos)) this->set(_pos, false);
        else this->set(_pos, true);
    }
    
                                                        //changing functions
    std::string to_string() {
        std::string _ret;
        _ret.clear();
        for (rg int i = 0; i < _Size; ++i) {
            for (rg int j = _BitNum - 1; ~j; --j) _ret += (this->_Ary[i] & (1ull << j)) ? '1' : '0';
        }
        return _ret;
    }
    
    unsigned int to_ulong() {
        return this->_Ary[_Size - 1];
    }
};
}	//namespace

参考资料

https://www.cnblogs.com/magisk/p/8809922.html

https://blog.youkuaiyun.com/linkfqy/article/details/75578669

https://blog.youkuaiyun.com/aolian4963/article/details/101947150

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

RWLinno

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值