算法--位运算

位运算介绍:

位运算是一种对二进制数的位进行操作的运算方式。

它直接对二进制数的每一位进行逻辑操作,而不考虑整个数的数值大小,一般情况下,位运算中每一位都相互独立,各自运算得出结果(左右移除外)

在计算机科学和编程中,位运算常用于优化算法、位掩码操作、位字段处理等领域;

在竞赛中,位运算经常考察异或的性质、状态压缩、与位运算有关的特殊数据结构、构造题等
注意:位运算只能应用于整数,且一般为非负整数,不能应用于字符、浮点等类型。

位运算是对二进制经行操作的基础,我把它分为位运算基础和位运算技巧:

1,<位运算基础>

位运算基础有与(&),或(|),异或(^),按位取反(~),按位左移(<<),按位右移(>>);

//位运算
//位运算--与&
//只有当两个位都为1时结果位才为1,否则为0;

//位运算--或|
//只要两个位其中有一个为1,结果位就为1,否则为0;

//位运算--异或^ 
//当两个位不同时,结果位为1,否则为0;

//位运算--按位取反~
//通常用于无符号整数(unsigned)避免符号位取反造成干扰
//即0变1,1变0;

//位运算--按位左移<<
//移动后低位补0;
//左移操作相当于对原数经行乘以2的幂次方的操作。

2,<位运算技巧>

位运算技巧分为判断数字奇偶性,获取二进制数的某一位,修改二进制中的某一位为1,快速判断一个数字是否为2的幂次方,获取二进制位中最低位的1;


//位运算技巧--判断数字奇偶性
//公式--x&1
//如果结果为1,说明是奇数,结果为0说明是偶数

//位运算技巧--获取二进制数的某一位
//公式:x>>i&1
//结果必然为0或1,表示x的二进制表示中的第i位;

//位运算技巧--修改二进制中的某一位为1;
//公式:x|(1<<i)
//将x的第i位或上1,则下x[i ]变为1,其他位上或上0,没有影响。

//快速判断一个数字是否为2的幂次方;
//公式:x&(x-1)
//如果x为2的幂次方,则x的二进制表示中只有一个1,x-1就有很多个连续的1,并且和x的1没有交集,两则与运算一定为0,可以证明其他情况必然不为0;
//  x=001000
//x-1=000111 

//位运算技巧--获取二进制位中最低位的1;
//公式:lowbit(x)=x&-x
//如果x=(010010),则lowbit(x)=(000010) ;
//常用于数据结构树状数组中。

下面直接开始上栗子:

eg1:lanqiao OJ 1331 二进制中的1

题目分析:

  需要判断二进制里面1的个数,方法其实有很多,这里我们展示的是用或运算计算结果,每次循环判断最后一位是不是1,是的话就累加;

看代码:

//位运算-- lanqiaoOJ 1331 
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+9;
int main()
{
	//注意这里的int类型要用unsigned
   unsigned int x;cin>>x;
	int ans=0;
	while(x)
	{
		if(x&1)
		ans++;
    //向右移动一位
		x>>=1;
	}
	cout<<ans;
	return 0;
}

eg2:位运算--lanqiao OJ 3691 区间域 

题目分析:

  这道题我觉得其实是有一定难度的,但是蓝桥云给它的难度判定是简单哈哈哈!

直接看题,题目大概意思是说给我们一个区间[l,r],然后我们再把l到r的数进行或运算,思路很简单,题目数列给的数我们可以把它看成一个32位的二进制数,肯定是这32位1和0来经行或运算,这里我们可以联想到前缀和,从第一位开始,若相加大于0,这或运算之后一定等于1;

看代码;

//位运算--lanqiao OJ 3691 区间域 
//需要用到几个前面讲到的位运算技巧;
// 
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+9;
int a[N];
//前缀和;
int prefix[35][N];
int main()
{
	ios::sync_with_stdio(0),cin.tie(0),cout.tie(0);
	int n,q;cin>>n>>q;
	int a[n+1];
	for(int i=1;i<=n;i++)
	cin>>a[i];
	for(int w=0;w<=30;w++)
	{
		for(int i=1;i<=n;i++)
		{
			prefix[w][i]=prefix[w][i-1]+(a[i]>>w&1);
		}
	}
	while(q--)
	{
		int l,r;
		cin>>l>>r;
	int ans=0;
	//w代表的是多少第多少位数 
		for(int w=0;w<=30;w++)
		{
			//r---l-1注意一下
			//(1<<w)代表2的w次幂; 
			ans+=(1<<w)*((prefix[w][r]-prefix[w][l-1])?1:0); 
		}
 	cout<<ans<<endl;		
	}
	return 0;
}

eg3:lanqiao OJ 3400 异或森林

题目解析:

  这道题就比较难了,他不仅运用到位运算技巧,同时还需要你有一定的逻辑思考能力;

我们一层一层的分析一下题目:

首先如何判断一个数的因数是不是偶数个:

用图画画的,有一点简单,但是我们其实很明显可以发现,一个数的因数在√x(x代表这个数)两侧是成对存在的,所以我们只需要判断√x是不是整数就行了,再换位思考一下,如果√x是整数的话,不就代表这个数是可以开平方根吗,所以我们得出结论,若因数个数为偶数,则该数为完全平方数;即1,4,9,16等等;

  本题题意是要找出因数个数为偶数的数,也就是找出非完全平方数,但是非完全平方数肯定是远远多与完全平方数的,找多不如找少,所以我们何不换位思考一下,找出完全平方数,然后用总数减去该数,不就得出答案了吗;

下面看代码:

//位运算--lanqiao OJ 3400 异或森林 
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+9;
int a[N];
int perfix[N];
int ant[N];
int main()
{
	//数组长度n; 
    int n;
    cin>>n;
    //输入数组 
    for(int i=1;i<=n;i++)
    cin>>a[i];
    //这里同样运用了前缀和的原理: 
    for(int i=1;i<=n;i++)
    {
    	perfix[i]=perfix[i-1]^a[i];
	}
	ant[0]=1;
	//总数(子区间的数量) 
	int ans=n*(n+1)/2;
	for(int i=1;i<=n;i++)
	{
		//异或运算不改变长度,题目给出:a[i]最大为10^4,a[i]^a[i+1]<=2*10^4;2*10^4开方约运算为200; 
		for(int j=0;j<=200;j++)
		{
			int sq=j*j;
			//这里很有意思,这里用了异或计算的一个技巧:
			//a^b=c;
			//a^b^b=c^b;
			//a=c^b;
			//所以同理,这里我们就可以perfix[i]^sq看作是完全平方数; 
			ans-=ant[perfix[i]^sq];
		}
		//这里用的很妙,自行体会; 
		ant[perfix[i]]++;
	}
	cout<<ans;
	return 0;	
} 

位运算的介绍就暂时到这里吧

  • 10
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值