思路來源
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單次操作時間複雜度,空間方面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) https://ac.nowcoder.com/acm/contest/82/E
還是這道題,相對好寫一點的寫法,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) https://www.acwing.com/problem/content/description/166/
題目:給定一張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;
}