位运算技巧
1位运算
- 位运算的运算速度较快
- 在状态压缩dp中,如果以2进制表示集合,位运算就是基于集合的操作。
- 关于位运算的一些题目需要用到位运算的一些性质
2性质
每个进制位的加减运算都可以独立进行
a&(b|c) = (a&b)|(a&c)
a^(b|c)=(a ^ b) | (a ^ c)
(a|b)|c = a|(b|c)
(a&b)&c = a&(b&c)
(a^ b)^ c = a^(b ^c)
a|0 = a
a&1 = a
a&0 = 0
a^a = 0
a^0 =a
a|~a = 1
a&~a = 0
a&a = a
a|a = a
a|(a&b) = a
a&(a|b) = a
3例题
dp二进制状态压缩:
集合常用处理手段:
- (n>>k)&1 取出n第k位的数
- n&((1<<k)-1) 取出n 0至k-1位的数
- n^(1<<k) n第k位取反
- n|(1<<k) n第k位取或
- n&(1<<k) n第k位取和
3.1
https://www.acwing.com/problem/content/93/
一道dp二进制压缩的题,题目解法基于对二进制压缩的理解和对集合操作的应用。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<sstream>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<deque>
#include<cstdlib>
#include<ctime>
#define dd double
#define ll long long
#define ull unsigned long long
#define N 21
#define M number
using namespace std;
const int INF=0x3f3f3f3f;
int n,a[N][N];
ll f[(1<<20)+1][21],ans=INF;
inline int Min(int a,int b){
return a>b?b:a;
}
/*inline void dfs(int zh,int x){
if(zh&(1<<(n-1))){
// printf("enter_end\n");
if(zh+1!=(1<<n)) return;
else ans=Min(ans,f[zh][x]);
// printf("new_ans\n");
return;
}
for(int i=0;i<=n-1;i++){
if((zh&(1<<i))==0&&f[zh][x]+a[x][i]<f[zh|(1<<i)][i]){
f[zh|(1<<i)][i]=f[zh][x]+a[x][i];
dfs(zh|(1<<i),i);
}
}
}*/
int main(){
scanf("%d",&n);
for(int i=0;i<=n-1;i++)
for(int j=0;j<=n-1;j++)
scanf("%d",&a[i][j]);
memset(f,INF,sizeof(f));
f[1][0]=0;
for(int i=1;i<=((1<<n)-1);i++){
int k=i;
for(int j=0;j<=n-1;j++){
if(!(i&(1<<j))) continue;
for(int q=0;q<=n-1;q++){
if(i&(1<<q)) continue;
f[i|(1<<q)][q]=Min(f[i|(1<<q)][q],f[i][j]+a[j][q]);
}
}
}
// dfs(1,0);
printf("%lld",f[(1<<n)-1][n-1]);
return 0;
}
3.2
https://www.acwing.com/problem/content/description/1000/
充分利用了二进制可以按位处理的性质,对每一位进行拆分,有效降低时间复杂度,裁剪状态空间。
#include<iostream>
#include<cstdio>
#include<cmath>
#include<algorithm>
#include<cstring>
#include<sstream>
#include<queue>
#include<map>
#include<vector>
#include<set>
#include<deque>
#include<cstdlib>
#include<ctime>
#define dd double
#define ld long double
#define ll long long
#define ull unsigned long long
#define N 100100
#define M number
using namespace std;
struct rode{
string s;
ll num;
};
rode a[N];
ll n,m,ans;
inline ll ceshi(ll num,ll shu){
for(int i=1;i<=n;i++){
if(a[i].s=="AND") shu&=((a[i].num>>num)&1);
else if(a[i].s=="OR") shu|=((a[i].num>>num)&1);
else if(a[i].s=="XOR") shu^=((a[i].num>>num)&1);
}
return shu;
}
int main(){
scanf("%lld%lld",&n,&m);
for(int i=1;i<=n;i++){
cin>>a[i].s;
scanf("%lld",&a[i].num);
}
for(ll i=30;i>=0;i--){
if((1<<i)>m) continue;
ll one=ceshi(i,1);
ll zero=ceshi(i,0);
if(one==1&&zero==0) ans+=(1<<(ll)i);
}
for(int i=1;i<=n;i++){
if(a[i].s=="AND") ans&=(a[i].num);
else if(a[i].s=="OR") ans|=(a[i].num);
else if(a[i].s=="XOR") ans^=(a[i].num);
}
printf("%lld",ans);
return 0;
}
4.成对变化
主要对于xor来说
n为偶数 n xor 1=n+1
n为奇数 n xor 1=n-1
5.lowbit
lowbit(n)表示n在二进制下最低位的1及其后面的0所组成的数。
求法:lowbit(n)=n&(~n+1)=n&(-n);
lowbie配合哈希可以求出整数二进制下所有是1的位数。
技巧:当k在0到35时,2的k此方mod37互不相等。
int h[37];
int main()
{
for(int i=0;i<36;i++) h[(1(ll)<<i)%37]=i;
while(cin>>n){
while(n>0){
cout<<h[(n&-n)%37]<<" ";
n-=n&-n;
}
cout<<endl;
}
}
```cpp