位运算


\(bit\)是度量信息的单位,包含\(0\)\(1\)两种状态。计算机的各种运算最后无不归结为一个个\(bit\)的变化。熟练掌握并利用位运算,能够帮助我们理解程序运行中的种种表现,提高程序运行时的时空效率,降低编程时间复杂度。

——选自《算法进阶指南》


而如此功能的位运算,常见的无非也就六种:

1. 与(&) 2.或(|) 3.非(~)

4.异或(^) 5.左移(<<) 6.右移(>>)

至于功能,大家都很熟了,就不再赘述。

OK,位运算讲解结束,全篇完!


还是来讲讲例题吧,不然毫无感觉。。。


例题1:P6225 异或橙子

一句话题意:求一个序列区间中所有子区间异或和的异或和。

好像有点拗口?就是说:

一区间为\(a_1\)~\(a_3\),

\(ans~=~a_1~xor~a_2~xor~a_3~xor~(a_1~xor~a_2)~xor~(a_2~xor~a_3)~xor~(a_1~xor~a_2~xor~a_3)\)

第一眼看过去,就三个字:不可做!但我仔细看了看,有一个惊人发现:我还是不会。

没办法,找找规律试试:

1.区间为\(a_1\)~\(a_3\Rightarrow~ans=a_1~xor~a_3\)

2.区间为\(a_1\)~\(a_4\Rightarrow~ans=0\)

3.区间为\(a_1\)~\(a_5\Rightarrow~ans=a_1~xor~a_3~xor~a_5\)

4.区间为\(a_1\)~\(a_6\Rightarrow~ans=0\)

突然我又有了惊人的发现:区间长度为偶数时答案为\(0\),区间长度为奇数时答案为所有下标为奇数的权值的异或和!

然后现实又给了我一巴掌:

5.区间为\(a_2\)~\(a_7\Rightarrow~ans=a_2~xor~a_4~xor~a_6\)

这次错不了,答案为区间内所有与端点下标奇偶性相同的权值的异或和。(端点不同则答案为\(0\)

有了这个性质,我们就可以用两颗线段树或树状数组,保存奇数和偶数的异或和,进行查询和修改

然后。。没有然后了。。。

呸,然后手起,码落:

#include<bits/stdc++.h>
#define re register
#define lowbit(x) ((x)&(-x))
using namespace std;
const int N=200005;
int n,m,a[N],val[N][2];
inline void add(int x,int y,bool yor)
{
	for(;x<=n;x+=lowbit(x))
		val[x][yor]^=y;
}
inline int ask(int x,bool yor)
{
	re int sum=0;
	for(;x>0;x-=lowbit(x))
		sum^=val[x][yor];
	return sum;
}
int main()
{
	scanf("%d%d",&n,&m);
	for(re int i=1;i<=n;i++)scanf("%d",&a[i]),add(i,a[i],i&1);
	for(re int type,l,r;m--;)
	{
		scanf("%d%d%d",&type,&l,&r);
		if(type==1)	add(l,a[l],l&1),add(l,r,l&1),a[l]=r;
		else printf("%d\n",((r-l+1)&1)?ask(r,r&1)^ask(l-2,l&1):0);
	}
	return 0;
}

再来一道绝世好题咋样???

例题2:P4310 绝世好题

还是一句话题意:求一段长度为\(n\)区间的最长子区间长度,使其满足\(a_i~\&~a_{i+1}!=0\)

有没有感觉似曾相识?好像最长上升子序列是变成了最长“与后非零”子序列。

\(n\le10^5\),没法与最长上升子序列一样\(O(n^2)\)求答案,还是毫无头绪。。

不对,曾记否?学长说过,它\(O(nlogn)\)也能求!

但蒟蒻的我从未打过\(O(nlogn)\)求最长上升子序列。。

慢着,等我去学学再来。

所以各位读者:休息一下,马上回来!

OK,咱们继续,

\(O(nlogn)\)的最长上升子序列与之前不同,保存的不是\(a_i\)最大能排在第几位,而是排在第\(i\)位的数最小可以为多少。

那我们依葫芦画瓢,保存。。(额,还是不会

突然想起来这章讲的是位运算!与的运算法则又是同一为一,其余为零,那我们保存子序列最后一项二进制下第\(i\)位为\(1\)的最大长度,然后转移方程就出来了:

\(f_j=max{f_j}+1(0\le j\le 32)\)(\(a_i\)在二进制下第\(j\)位为\(1\))

然后我们就A了一道绝世好题。。

手起,码落:

#include<bits/stdc++.h>
#define re register
using namespace std;
int n,a,f[40],ans;
int main()
{
	scanf("%d",&n);
	for(re int i=1,maxx;i<=n;i++)
	{
		scanf("%d",&a);maxx=0;
		for(re int j=0;j<=32;j++)
			if((a>>j)&1)maxx=max(maxx,f[j]+1);
		for(re int j=0;j<=32;j++)
			if((a>>j)&1)f[j]=maxx;
	}
	for(re int i=0;i<=32;i++)ans=max(f[i],ans);
	printf("%d",ans);
	return 0;
}

讲完了,不容易呀,给个赞在走不,QWQ~

posted @ 2020-08-28 08:03  Chester1011  阅读(209)  评论(0编辑  收藏  举报
/*simplememory*/