Loading

位运算技巧

1位运算

  1. 位运算的运算速度较快
  2. 在状态压缩dp中,如果以2进制表示集合,位运算就是基于集合的操作。
  3. 关于位运算的一些题目需要用到位运算的一些性质

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二进制状态压缩:

集合常用处理手段:

  1. (n>>k)&1 取出n第k位的数
  2. n&((1<<k)-1) 取出n 0至k-1位的数
  3. n^(1<<k) n第k位取反
  4. n|(1<<k) n第k位取或
  5. 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
posted @ 2021-02-21 20:54  hyl天梦  阅读(88)  评论(0编辑  收藏  举报