头文件:<bitset>
声明方法:std::bitset<N> s;
开在全局时,默认全为 0;开在局部时无法保证,需要使用 reset 函数清空。
可以使用字符串来初始化:
cpp1std::bitset<8> s(std::string("00110101"));
注意,字符串最右边的数字是低位,也就是上述赋值后的结果为:
下表 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |
---|---|---|---|---|---|---|---|---|
值 | 1 | 0 | 1 | 0 | 1 | 1 | 0 | 0 |
直接下标修改 s[pos]=x;
,时间复杂度
支持左右位移、与、或、异或,返回一个 bitset,如 std::bitset<N> k=s<<x;
,时间复杂度 , 为计算机字长,可以视为 。右移是逻辑右移。
可以与 cin 和 cout 一块使用。还是注意字符串最右边的数字是低位。cout 时会自动补 0
cpp12cin>>s; // 1101
cout<<s; // 000001101
reset()
:全部置为 0
to_string() -> string
:转为 string,高位补 0
to_ulong() -> unsigned int
:转为 unsigned int ,溢出的话会 re
to_ullong() -> unsigned long long
:转为 unsigned long long,溢出的话会 re
set
set()
:全部置为 1set(int index,bool value = true)
:将下标为 index 的位置置为 valuetest(int index) -> bool
:返回 index 位置的值
any() -> bool
:如果 bitset 内有 1 ,则返回 true,反之返回 false
none() -> bool
:如果 bitset 内有 1,则返回 false,反之返回 true
count() -> unsigned int
:返回 bitset 内 1 的个数,注意是无符号
flip
flip()
:所有位取反flip(int index)
:指定位置按位取反以上操作的单点操作都为 ,整体操作都为
https://www.luogu.com.cn/problem/P1537
题目描述
给出六个非负整数 ,其中 是价值为 的弹珠的个数。最大弹珠总数将达到 。判断能否把这些弹珠分成价值相等的两份。
笔记
显然可以直接多重背包做。
现在考虑 01 背包,朴素的 01 背包会超时,但我们可以使用 bitset 优化
转移方程:,倒序枚举
我们发现这个方程相当于把一个 01 串左移 位再与原 01 串取或
我们把 视为一个 bitset,则转移可以写成
时间复杂度
cpp12345678910111213141516171819202122232425262728293031323334#include<bits/stdc++.h>
#define ull unsigned long long
#define ls(k) (k)<<1
#define rs(k) (k)<<1|1
#define debug(x) cout<<#x<<"="<<x<<endl
using namespace std;
const int inf=0x3f3f3f3f3f3f3f3f;
const int mod=0;
#define _ 120004
typedef pair<int,int> pii;
int in[7];
signed main(){
//ios::sync_with_stdio(false);
int now;
while(true){
int sum=0;now++;
for(int i=1;i<=6;i++) scanf("%d",&in[i]),sum+=i*in[i];
if(!sum) break;
printf("Collection #%d:\n",now);
if(sum%2){
printf("Can't be divided.\n\n");
continue;
}
bitset<_> s;s.reset();s.set(0);
for(int i=1;i<=6;i++){
for(int j=1;j<=in[i];j++){
s|=(s<<i);
}
}
if(s.test(sum/2)) printf("Can be divided.\n\n");
else printf("Can't be divided.\n\n");
}
return 0;
}
https://www.luogu.com.cn/problem/P5020
题目描述
在一个完善的货币系统中,每一个非负整数的金额 都应该可以被表示出。然而, 货币系统可能是不完善的。例如在货币系统 , 中,金额 就无法被表示出来。
两个货币系统 和 是等价的,当且仅当对于任意非负整数 ,它要么均可以被两个货币系统表出,要么不能被其中任何一个表出。
现在网友们打算简化一下货币系统。他们希望找到一个货币系统 ,满足 与原来的货币系统 等价,且 尽可能的小。他们希望你来协助完成这个艰巨的任务:找到最小的 。
有多组测试数据。
数据范围
。
笔记
先去重,然后从小到大排序。如果这 个数中,一个数已经能被前面的数组合出来,那么这个数就可以去掉了。是一个完全背包问题。时间复杂度
完全背包也可以使用 bitset 优化。
对于一个物品 ,我们枚举这个物品的个数 ,转移方程即为 。将 dp 数组视为一个 bitset,则有 。
然后我们进一步考虑:
时相当于 和 按位或。
时相当于 、 、、 异或。
发现 是不需要枚举的,因为 和 的结果叠加了。同理 可以由 和 的结果叠加。我们发现只需要枚举 的幂次即可。
总的时间复杂度 ,优化了一点。
cpp123456789101112131415161718192021222324252627282930313233343536#include<bits/stdc++.h>
#define int long long
#define ull unsigned long long
#define ls(k) (k)<<1
#define rs(k) (k)<<1|1
#define debug(x) cout<<#x<<"="<<x<<endl
using namespace std;
const int inf=0x3f3f3f3f3f3f3f3f;
const int mod=0;
typedef pair<int,int> pii;
int in[101];
inline void subtask(){
int n,mx=-1;cin>>n;
int ans=n;
for(int i=1;i<=n;i++) cin>>in[i],mx=max(mx,in[i]);
sort(in+1,in+n+1);
bitset<25001> s;s.reset();s.set(0);
for(int i=1;i<=n;i++){
if(s.test(in[i])){
ans--;
continue;
}
int x=in[i];
while(x<=mx){
s|=(s<<x);
x*=2;
}
}
cout<<ans<<endl;
}
signed main(){
ios::sync_with_stdio(false);
int t;cin>>t;
while(t--) subtask();
return 0;
}
https://www.luogu.com.cn/problem/P3674
题目描述
个数, 个操作:
数据范围
笔记
没有修改,仅查询,支持离线,考虑莫队。考虑使用 bitset 维护:
如果询问是否存在 和 满足 ,即判断是否存在 。我们令 bitset 维护某个数是否存在,那么只需要看一下 是否存在 1 即可。
如果沿用上述的方法,需要判断 是否存在,直接用 无法实现,因为 前面有一个负号。我们可以考虑维护 ,但是 bitset 只能维护非负数,所以我们改为维护 ,其中 为值域上限,用 bitset 。
于是转化为判断 是否存在,那么只需要看一下 是否存在 1 即可。
可以用 的时间暴力枚举 的因数 ,然后判断 和 是否都存在即可。
cpp1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253#include<bits/stdc++.h>
#define ull unsigned long long
#define ls(k) (k)<<1
#define rs(k) (k)<<1|1
#define debug(x) cout<<#x<<"="<<x<<endl
using namespace std;
const int inf=0x3f3f3f3f3f3f3f3f;
const int mod=0;
typedef pair<int,int> pii;
#define _ 100005
const int N=100000;
int a[_],blo,bl[_],bu[_];
struct Q{int l,r,id,op,x,ans;} q[_];
bitset<_> s1,s2;
inline void add(int x){
bu[a[x]]++;
if(bu[a[x]]==1) s1.set(a[x]),s2.set(N-a[x]);
}
inline void del(int x){
bu[a[x]]--;
if(bu[a[x]]==0) s1.set(a[x],0),s2.set(N-a[x],0);
}
inline void subtask(){
int n,m;cin>>n>>m;
blo=sqrt(n);
for(int i=1;i<=n;i++) cin>>a[i],bl[i]=(i-1)/blo+1;
for(int i=1;i<=m;i++) cin>>q[i].op>>q[i].l>>q[i].r>>q[i].x,q[i].id=i;
sort(q+1,q+m+1,[](Q x,Q y){return bl[x.l]==bl[y.l]?x.r<y.r:bl[x.l]<bl[y.l];});
int l=1,r=0;
for(int i=1;i<=m;i++){
while(l<q[i].l) del(l++);
while(l>q[i].l) add(--l);
while(r<q[i].r) add(++r);
while(r>q[i].r) del(r--);
if(q[i].op==1) q[i].ans=(s1&(s1<<q[i].x)).any();
else if(q[i].op==2) q[i].ans=(s1&(s2>>(N-q[i].x))).any();
else if(q[i].op==3){
for(int j=1;j*j<=q[i].x;j++){
if(q[i].x%j) continue;
q[i].ans=s1[j]&s1[q[i].x/j];
if(q[i].ans) break;
}
}
}
sort(q+1,q+m+1,[](Q x,Q y){return x.id<y.id;});
for(int i=1;i<=m;i++) cout<<(q[i].ans?"hana":"bi")<<endl;
}
signed main(){
ios::sync_with_stdio(false);
int t=1;//cin>>t;
while(t--) subtask();
return 0;
}
https://codeforces.com/problemset/problem/914/F
题目描述
给定字符串 ,多次询问某个字符串 在 中出现了多少次,带修。,时限 6s
笔记
bitset 还可以用来乱搞字符串匹配
我们把字符串 中的每种字符都开一个 bitset,在 bitset 中记录该字符的出现位置,比如对于字符串 ababababab
则 bitset 为 0101010101
bitset 为 1010101010
(注意字符串最右边的数字是低位)
如果现在有字符串 aba
,我们想知道这个字符串在字符串 中出现了多少次,那么我们可以这样:先有一个bitset ,初始时该 bitset 中每一位都置为 1。然后我们枚举字符串 中的每个字符 ,再令 :
bitset | 位 |
---|---|
初始时 | 1111111111 |
0101010101 | |
0101010101 | |
0001010101 | |
按位与之后 | 0001010101 |
然后我们发现,最后得到的 中,有 的位置就表示这个位置可以作为字符串 起始位置。于是我们看一下有多少个 1,就知道了出现了多少次。时间复杂度 。如果 和 都是 级别,那么可以通过这个优化给草过去。相当于优化了暴力。
而本题还给定了区间,对于询问区间 (下标为 1 开始),我们取 的区间 (区间从右往左看)中的 1 的个数。
cpp1234567891011121314151617181920212223242526272829303132333435#include<bits/stdc++.h>
#define ull unsigned long long
#define ls(k) (k)<<1
#define rs(k) (k)<<1|1
#define debug(x) cout<<#x<<"="<<x<<endl
using namespace std;
const int inf=0x3f3f3f3f3f3f3f3f;
const int mod=0;
typedef pair<int,int> pii;
#define _ 100005
bitset<_> s[27];//index-1
char str[_];//index-0
inline void subtask(){
cin>>str;
for(int i=0;i<strlen(str);i++) s[str[i]-'a'][i+1]=1;
int m;cin>>m;
while(m--){
string ss;
int op,l,r;cin>>op>>l;
if(op==1) cin>>ss,s[str[l-1]-'a'][l]=0,str[l-1]=ss[0],s[str[l-1]-'a'][l]=1;
else{
cin>>r>>ss;
bitset<_> ans;ans.set();
for(int i=0;i<ss.size();i++) ans&=(s[ss[i]-'a']>>i);
int sum1=(ans>>l).count(),sum2=(ans>>max(r-(int)ss.size()+2,0)).count();
cout<<max(sum1-sum2,0)<<endl;
}
}
}
signed main(){
ios::sync_with_stdio(false);
int t=1;//cin>>t;
while(t--) subtask();
return 0;
}