bitset(知识整理+例题总结)

思路来源

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单次操作时间复杂度O(\frac{n}{64}),空间方面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求传递闭包,复杂度O(\frac{n^{3}}{64})

#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的再做一遍前缀和,回答询问时或一遍即可,复杂度大概为O(\frac{n*m}{64}+\frac{n}{64}*\sum a)

给唐老师的代码加点注释吧,毕竟自己不会写这种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用朴素的写法,复杂度大致O(n*m+\frac{n}{64}*\sum a)

小技巧: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图,先拓扑,倒序更新前驱,输出个数即可,复杂度O(\frac{n(n+m)}{64})

#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)

题解

Codeforces Round 890 (Div. 2) E2. PermuTree (hard version)(根号分治+二进制优化多重背包+不定长bitset优化01背包)(nsqrt/w)_Code92007的博客-CSDN博客

说明

做了一个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();
}

  • 4
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Code92007

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

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

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

打赏作者

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

抵扣说明:

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

余额充值