一直知道这个东西但具体还真没用过,今天来学习一下。
Bitset具体也没什么麻烦的操作,无非是一些简单的位运算。
用几道题来看一下具体适用的情况:
BZOJ3687 简单题
题意:给定n个正整数,求所有子集和的异或和。 n < 1000 , ∑ a i < = 2000000 n<1000, \sum ai<=2000000 n<1000,∑ai<=2000000。
思路:因为限制了所有元素和的上限,考虑对每种和求方案数,如用 f [ i ] f[i] f[i] 表示和为i的子集个数,显然可得 f [ i ] + = f [ i − a [ j ] ] f[i]+=f[i-a[j]] f[i]+=f[i−a[j]],因为对于答案异或和来说我们只需知道每种和的奇偶性,考虑用bitset来表达值域内每种和的奇偶性。
#include<bits/stdc++.h>
using namespace std;
bitset<2000005> cnt;
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
cnt[0]=1;
int n;
cin>>n;
for(int i=0;i<n;i++){
int x;
cin>>x;
cnt^=cnt<<x;
}
int ans=0;
for(int i=1;i<=2000000;i++){
if(cnt[i]==1) ans^=i;
}
cout<<ans<<endl;
}
BZOJ4484 最小表示
题意:给定一个有向无环图,求最多可以删掉多少边使得连通性不发生改变。
N
<
=
30000
,
M
<
=
100000
N<=30000,M<=100000
N<=30000,M<=100000
思路: 对于某一对点
(
x
,
y
)
(x,y)
(x,y) 路径之间的所有点,保证这条路径上连通性不发生改变,我们可以留下
(
x
,
y
)
(x,y)
(x,y) 的最长路,并将剩下的边都删掉。
或者考虑贪心一下,将原图拓扑排序之后,对于一个新加入的点
x
x
x,对于所有连入
x
x
x的边,我们先尝试保留另一端点拓扑序较大的点,如:对于两条边
(
u
,
x
)
(u,x)
(u,x)和
(
v
,
x
)
(v,x)
(v,x) ,
t
o
p
[
u
]
<
t
o
p
[
v
]
top[u]<top[v]
top[u]<top[v],如果原图有点
u
u
u能到达点
v
v
v和点
x
x
x,那么我们在处理完点
v
v
v之后显然已经保证了
u
,
v
u,v
u,v的连通性,此时如果先加入边
(
v
,
x
)
(v,x)
(v,x),即可保证连通性不改变。
剩下的就是对于联通性的维护了,因为是有向图不能使用并查集,考虑用bitset维护对于每个点来说其他点能否到达。(不知道为什么网上其他的题解非要倒着来做这道题,也没有一个博客是给出理由的,正着连通性不是也可以维护吗= =)
#include<bits/stdc++.h>
using namespace std;
const int maxn=3e4+5;
bitset<maxn> b[maxn];
vector<int> E[maxn],E2[maxn];
int cnt[maxn],top[maxn],tip[maxn],tot=0;
queue<int> q;
bool cmp(int x,int y){
return top[x]>top[y];
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int n,m;
cin>>n>>m;
for(int i=0;i<m;i++){
int x,y;
cin>>x>>y;
E[x].push_back(y);
E2[y].push_back(x);
cnt[y]++;
}
for(int i=1;i<=n;i++) if(cnt[i]==0) q.push(i);
while(!q.empty()){
int x=q.front();
q.pop();
top[x]=++tot;
tip[tot]=x;
for(auto i:E[x]){
cnt[i]--;
if(cnt[i]==0) q.push(i);
}
}
int res=0;
for(int i=1;i<=n;i++){
int x=tip[i];
b[x][x]=1;
sort(E2[x].begin(),E2[x].end(),cmp);
for(auto j:E2[x]){
if(b[x][j]==0) b[x]|=b[j];
else res++;
}
}
cout<<res<<endl;
}
HDU6268
题意:给定一棵N个点的树,每个点有点权,求是否存在权值和分别为1~M的子图。
N
<
=
3000
,
M
<
=
100000
N<=3000,M<=100000
N<=3000,M<=100000
思路:点分治能解决树的子图问题是一个基本套路了,接下来就是一个树形依赖背包问题,如果直接做的话复杂度是 O ( N M l o g N ) O(NMlogN) O(NMlogN),让人有点接受不了,因此考虑用bitset优化背包。
#include<bits/stdc++.h>
using namespace std;
const int maxn=3005,inf=0x3f3f3f3f;
bitset<100005> ans,dp[maxn];
vector<int> E[maxn];
int a[maxn];
int tip[3005],siz2[3005],tot;
int siz[maxn],maxs[maxn],vis[maxn];
int rt,all;
void dfs_rt(int x,int fa){
siz[x]=1,maxs[x]=0;
for(auto i:E[x]){
if(vis[i] || i==fa) continue;
dfs_rt(i,x);
siz[x]+=siz[i];
maxs[x]=max(maxs[x],siz[i]);
}
maxs[x]=max(maxs[x],all-siz[x]);
if(maxs[x]<maxs[rt]) rt=x;
}
void dfs_dis(int x,int fa){
tip[++tot]=x;
siz2[x]=1;
for(auto i:E[x]){
if(vis[i] || i==fa) continue;
dfs_dis(i,x);
siz2[x]+=siz2[i];
}
}
void dfz(int x){
maxs[0]=inf;
rt=0,all=siz[x];
dfs_rt(x,x);
vis[rt]=1;
tot=0;
dfs_dis(rt,rt);
for(int i=1;i<=tot;i++) dp[i]=0;
dp[tot+1]=1;
for(int i=tot;i>=1;i--){
int x=tip[i];
dp[i]=dp[i+siz2[x]]|(dp[i+1]<<a[x]);
}
ans|=dp[1];
for(auto i:E[rt]){
if(!vis[i]) dfz(i);
}
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int T;
cin>>T;
while(T--){
int n,m;
cin>>n>>m;
for(int i=1;i<=n;i++) vis[i]=0,E[i].clear();
ans=0;
for(int i=1;i<n;i++){
int x,y;
cin>>x>>y;
E[x].push_back(y);
E[y].push_back(x);
}
for(int i=1;i<=n;i++) cin>>a[i];
siz[1]=n;
dfz(1);
for(int i=1;i<=m;i++) cout<<ans[i];
cout<<'\n';
}
}