bitset(知識整理+例題總結)

思路來源

https://www.cnblogs.com/RabbitHu/p/bitset.html 胡小兔的博客

https://blog.csdn.net/qq_40772738/article/details/81301794 bitset優化

https://ac.nowcoder.com/acm/contest/view-submission?submissionId=24176917 唐老師的提交代碼

知識整理

卡常大法好,神器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) https://ac.nowcoder.com/acm/contest/82/E

還是這道題,相對好寫一點的寫法,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) https://www.acwing.com/problem/content/description/166/

題目:給定一張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;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章