思路来源
https://www.cnblogs.com/RabbitHu/p/bitset.html 胡小兔的博客
【破烂集】大佬的一个技巧——bitset优化_bitset 取交速度慢_7376798669717289的博客-CSDN博客 bitset优化
登录—专业IT笔试面试备考平台_牛客网 唐老师的提交代码
遍历bitset中的1:_Find_first和_Find_next_Qres821的博客-CSDN博客
知识整理
卡常大法好,神器bitset,存二进制位,类似bool数组的作用,
长度n单次操作时间复杂度,空间方面bitset中一位占1 bit,相当于char空间的1/8,
下标从0开始,整数和bool[]数组都可转bitset,
大小需要编译时确定,否则vector<bool>(奇怪的用法orz),
以下是bitset的一些常用用法,转化为ul或ull时,需保证不会溢出,不然会报overflow
#include<iostream>
#include<cstdio>
#include<bitset>
#include<cstring>
using namespace std;
const int N=15;
bitset<N>x,y,z(255),a(0xfa),b(string("0101"));
bitset<32>bs(2147483647);
int main(){
x[0]=1;x<<=1;y>>=1;
x^=y;x|=y;x&=y;x=~y;
puts(x==y?"Yes":"No");
puts(x!=y?"Yes":"No");
printf("%s\n",x.any()?"Yes":"No");//是否有1
printf("%s\n",x.none()?"Yes":"No");//是否全0
printf("%d\n",x.count());//1的个数
printf("%d\n",x.size());//bitset长度
printf("%s\n",x[0]==1?"Yes":"No");//下标从0起
printf("%s\n",x.test(1)?"Yes":"No");//x[1]
x.set();x.reset();x.flip();
x.set(1,0);//加第二维参可以赋0 否则默认赋1
y.set(5);y.reset(5);y.flip(5);
cout<<x<<endl;//输出x的01序列
printf("%llu\n",x.to_ulong());//unsigned long
printf("%llu\n",z.to_ullong());//unsigned long long
string c=b.to_string();//string
cout<<c<<endl;
//将int转化为二进制序列,强行偷懒
int v=2147483647;
bitset<32>d(v);
cout<<d<<endl;//首位符号位
string e=d.to_string().substr(1);
cout<<e<<endl;//不含符号位
return 0;
}
for(k = bit._Find_first() ; k < M ; k = bit._Find_next(k)),
链式前向星的操作可以拓展到bitset里,每次找到lowbit,见下面的压位bfs题目
压位floyd
以bzoj2208 [Jsoi2010]连通数 为例,当然正解不是这个
floyd求传递闭包,复杂度
#include <bits/stdc++.h>
using namespace std;
const int N=2e3+5;
int n,ans;
char s[N];
bitset<N>dp[N];
int main(){
scanf("%d",&n);
for(int i=1;i<=n;++i){
scanf("%s",s+1);
for(int j=1;s[j];j++){
if(s[j]=='1')dp[i].set(j);
}
dp[i].set(i);
}
for(int k=1;k<=n;++k){
for(int i=1;i<=n;++i){
if(dp[i][k])dp[i]|=dp[k];
}
}
for(int i=1;i<=n;++i){
ans+=dp[i].count();
}
printf("%d\n",ans);
return 0;
}
压位bfs
牛客练习赛14 无向图中的最短距离(bfs+bitset)
题目:n(n<=1e3)个点m(m<=1e5)条边的无向图,q(q<=1e5)次查询,
每次查询给出a(所有a之和<=210W)个点对(xi,yi),令dist(x,y)表示x和y点在图中最短距离,
dist(x,x)=0,如果x,y不连通则dist(x,y) = inf,
每次查询图中有多少个点v与至少一个这次询问给出的(xi,yi)满足dist(v,xi)<=yi
压位bfs,bitset<N>dp[i][j]维护起点是i在图上的距离<=j的时候能到的点集,
cur代表前j-1步的可达点集,nxt代表前j步的可达点集,tmp代表仅第j步的可达点集,
若cur==nxt,说明第j步无法再拓展,此时break即可,
抄一个唐老师用bitset实现类似链式前向星的head[u]、next[u]的板子,每次找到这一段的lowbit
先求=j的再做一遍前缀和,回答询问时或一遍即可,复杂度大概为
给唐老师的代码加点注释吧,毕竟自己不会写这种orz
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1001, maxd = 1 << 16 | 1;
int lbt[maxd];
//lowbit(bitset,low,upp) 求该bitset的[low,upp]内的lowbit
int lowBit(bitset<maxn> const &msk, size_t const &low, size_t const &upp) {
typedef unsigned long long _WordT;
_WordT *seq = (_WordT *)&msk;
size_t pL = low >> 6, pR = upp >> 6;
size_t qL = low & 63, qR = upp & 63;
for(size_t i = pL; i <= pR; ++i) {
_WordT val = seq[i];
if(i == pR && qR < 63)
val &= (static_cast<_WordT>(1) << (qR + 1)) - 1;
if(i == pL)
val = (val >> qL) << qL;
if(val != static_cast<_WordT>(0)) {
size_t ret = i << 6;
if((val & ((static_cast<_WordT>(1) << 32) - 1)) == static_cast<_WordT>(0)) {
val >>= 32;
ret |= 32;
}
if((val & ((static_cast<_WordT>(1) << 16) - 1)) == static_cast<_WordT>(0)) {
val >>= 16;
ret |= 16;
}
return ret + lbt[static_cast<int>(val & ((static_cast<_WordT>(1) << 16) - 1))];
}
}
return -1;
}
int n, m, q;
//dis[i][j]:表示源点为i距离<=j的可达点集
bitset<maxn> e[maxn], dis[maxn][maxn], cur, nxt, tmp;
int main() {
//lbt[i]表示i的二进制表示中lowbit在右起第几位(0-index) 用于lowbit函数的初始化
lbt[0] = -1;
for(int i = 1; i < maxd; ++i)
lbt[i] = i & 1 ? 0 : lbt[i >> 1] + 1;
scanf("%d%d%d", &n, &m, &q);
while(m--) {
int u, v;
scanf("%d%d", &u, &v);
--u, --v;
e[u].set(v);
e[v].set(u);
}
for(int i = 0; i < n; ++i) {
dis[i][0].set(i);
cur = dis[i][0];//cur:前0步可达
nxt = cur | e[i];//nxt:前1步可达
for(int j = 1; j < n; ++j) {
tmp = nxt ^ cur;//仅第j步可达
cur = dis[i][j] = nxt;//前j步可达
for(int u = lowBit(tmp, 0, n - 1); u != -1; u = lowBit(tmp, u + 1, n - 1))
nxt |= e[u];//更新第j+1步的情形
if(cur == nxt) {//前j步==前j+1步 说明无法再拓展
for(int k = j + 1; k < n; ++k)
dis[i][k] = cur;
break;
}
}
}
while(q--) {
int c, u, v;
cur.reset();
scanf("%d", &c);
while(c--) {
scanf("%d%d", &u, &v);
cur |= dis[--u][min(v, n - 1)];
}
printf("%d\n", (int)cur.count());
}
return 0;
}
维护点集的并集方案
很暴力的思想,或一下就可
牛客练习赛14 无向图中的最短距离(bfs+bitset) 登录—专业IT笔试面试备考平台_牛客网
还是这道题,相对好写一点的写法,bfs用朴素的写法,复杂度大致
小技巧:memset(127)是一个2e9左右的INF,memset(128)是一个-2e9左右的-INF
#include<bits/stdc++.h>
using namespace std;
#define pb push_back
const int N=1e3+2;
int n,m,q,x,y,k,d[N];
bitset<N>dp[N][N],res;
vector<int>e[N];
void bfs(int s){
memset(d,127,sizeof d);
queue<int>q;
q.push(s);
d[s]=0;
int x;
while(!q.empty()){
x=q.front();q.pop();
dp[s][d[x]].set(x);
for(auto &v:e[x]){
if(d[v]>n){
d[v]=d[x]+1;
q.push(v);
}
}
}
for(int i=1;i<n;++i){
dp[s][i]|=dp[s][i-1];
}
}
int main(){
scanf("%d%d%d",&n,&m,&q);
for(int i=1;i<=m;++i){
scanf("%d%d",&x,&y);
e[x].pb(y),e[y].pb(x);
}
for(int i=1;i<=n;++i){
bfs(i);
}
while(q--){
res.reset();
scanf("%d",&k);
while(k--){
scanf("%d%d",&x,&y);
y=min(y,n-1);//dis<=n-1
res|=dp[x][y];
}
printf("%d\n",res.count());
}
return 0;
}
AcWing 164.可达性统计(拓扑排序+bitset) 164. 可达性统计 - AcWing题库
题目:给定一张N个点M条边(1<=N,M<=3e4)的有向无环图,分别统计从每个点出发能够到达的点的数量。
题解:bitset<N>a[i]维护ai能到的点集,保证dag图,先拓扑,倒序更新前驱,输出个数即可,复杂度
#include<bits/stdc++.h>
using namespace std;
const int N=3e4+10;
int n,m,x,y,u,t,in[N],q[N];
vector<int>e[N];
bitset<N>a[N];
int main(){
scanf("%d%d",&n,&m);
for(int i=1;i<=m;++i){
scanf("%d%d",&x,&y);
e[x].push_back(y);
in[y]++;
}
for(int i=1;i<=n;++i){
if(!in[i])q[t++]=i;
}
for(int s=0;s<t;++s){
u=q[s];
for(auto &v:e[u]){
if((--in[v])==0){
q[t++]=v;
}
}
}
for(int i=t-1;i>=0;--i){
u=q[i];
a[u][u]=1;
for(auto &v:e[u]){
a[u]|=a[v];
}
}
for(int i=1;i<=n;++i){
printf("%d\n",a[i].count());
}
return 0;
}
非定长bitset写法(补充)
题目
Codeforces Round 890 (Div. 2) E2. PermuTree (hard version)
题解
说明
做了一个nsqrt(n)/w bitset优化背包的题,
每次bitset需要指定长度为传>x的最小长度
可以通过模板函数+递归的方式实现
因为模板函数的参数是个const,但又支持通过传入时指定参数
template<int LEN>void solve(){
if(LEN<=tot){solve<min(N,LEN<<1)>();return;}}
solve<1>();
#include <bits/stdc++.h>
using namespace std;
const int N=1000013;
typedef long long ll;
vector<int>mp[N];
int p[N],sz[N];
ll ans=0;
vector<int>sizes;
int tot=0;
int c[1010];
template<int LEN>void solve(){
if(LEN<=tot){solve<min(N,LEN<<1)>();return;}
bitset<LEN>B;B[0]=1;
const int SB=(int)sqrtl(tot);
vector<int>size2;
for(auto s:sizes)
if(s<SB)c[s]++;
else size2.push_back(s);
for(int i=1;i<SB;i++)if(c[i]){
int w=(c[i]-1)/2;
if(i*2>=SB){
for(int j=1;j<=w;++j){
size2.push_back(i*2);
}
}
else{
c[i*2]+=w;
}
c[i]=1+(c[i]-1)%2;
for(int j=1;j<=c[i];++j)size2.push_back(i);
c[i]=0;
}
for(auto s:size2)B|=B<<s;
ll mx=0;
for(int i=0;i<=tot;i++)if(B[i])mx=max(mx,(ll)(tot-i)*i);
ans+=mx;
}
void dfs(int x,int fa){
sz[x]=1;
for(auto i:mp[x])if(i^fa)dfs(i,x),sz[x]+=sz[i];
for(auto i:mp[x])if(i^fa)sizes.push_back(sz[i]);
tot=sz[x]-1;
for(auto s:sizes)if(s>=tot/2){
ans+=(ll)s*(tot-s);sizes.clear();
return;
}
solve<1>(), sizes.clear();
}
void solve(){
int n=1000000;
cin>>n;
for(int i=2;i<=n;i++){
cin>>p[i];
mp[p[i]].push_back(i);
}
dfs(1,0);
cout<<ans<<'\n';
}
signed main() {
cin.tie(nullptr);ios::sync_with_stdio(false);
int T=1;
//cin>>T;
while(T--)solve();
}