位运算的小整理
备注:以下^符号除程序中均为次方而非异或
1:基础运算符&优先级
加减(+ -)> 移位(<< >>) >比较大小(> < != ==)>位与(&)>异或(cpp ^)>位或(|)
2:memset函数:字节赋值 比如赋值整形 则只能赋值出每8位(2进制位)一样的数(0,0x3f)
3:快速幂:
即将a^b快速计算出的算法 朴素算法下 需要 b次累乘
快速幂 将b写成2进制并分解为(2^ai)形式
由a的(m+n)次方等于a的m次方乘a的n次方 从而进行 log2(b)次累乘解决
#include<iostream>
using namespace std;
long long qsm(long long ,long long ,long long);
int main()
{
long long a,b,p,ans;
cin>>a>>b>>p;
if (b!=0)
{
ans=qsm(a,b,p);
cout<<ans<<endl;
}
else cout<<1%p<<endl;
return 0;
}
long long qsm(long long a,long long b ,long long p)
{
long long rans, t,i;
i=a%p;
rans=1;
while (b!=0)
{
if (b&1) rans=(rans*i)%p;
i=(i*i)%p;
b=b>>1;
}
return rans;
}
4:与快速幂类似的64位余数乘法算法
直接乘会爆掉 又不想高精度 就把b二进制分解为2^ai
算a*2^ai的和 每次求和
#include<iostream>
using namespace std;
long long wc(long long ,long long ,long long);
int main()
{
long long a,b,p,ans;
cin>>a>>b>>p;
ans=wc(a,b,p);
cout<<ans<<endl;
return 0;
}
long long wc(long long a,long long b ,long long p)
{
long long rans, t,i;
i=a%p;
rans=0;
while (b!=0)
{
if (b&1) rans=(rans+i)%p;
i=(i*2)%p;
b=b>>1;
}
return rans;
}
5:二进制状态压缩
众所周知,一个bool只能表示1 0两个状态,却占用了大量空间!是极大的浪费!相比之下, int类型真是勤俭持家。所以我们不难想到:把长度为n的bool数组转化成0-2^n-1的int, 从而节约时间和空间。
相对应的,为了从转化的int数中直接读取或修改bool数组某项,我们可以用位运算。
操作 | 对应位运算 |
---|---|
取出n的2进制第k位: | (n>>k)&1 |
取出n的2进制0-k-1位 : | n&(1<<k) |
n的2进制第k位取反 : | n^(1<<k) |
n的2进制第k位赋值1 : | n位或(1<<k) |
n的2进制第k位赋值1 : | n&(~(1<<k)) |
例子:最短Hamilton路径:
代码:
#include<iostream>
#include<cstring>
using namespace std;
int weigh[30][30];
int f[1<<20][30];
int main()
{
int i,j,k,n;
memset(weigh,0,sizeof(weigh));
memset(f,0x3F,sizeof(f));
cin>>n;
for (i=0;i<n;i++)
for (j=0;j<n;j++)
{
cin>>weigh[i][j];
}
f[1][0]=0;
for (i=0;i<1<<n;i++)
for (j=0;j<n;j++)
{
if (i>>j&1)
{
for (k=0;k<=n;k++)
{
if (((i^1<<j)>>k)&1)
if (f[i][j]>f[i^1<<j][k]+weigh[j][k]) f[i][j]=f[i^1<<j][k]+weigh[j][k];
}
}
}
cout<<f[(1<<n)-1][n-1]<<endl;
return 0;
}
6:在无向图邻接表创建时存储反向边
2k xor 1=2k+1;
2k+1 xor 1=2k;s
从而实现
7:lowbit运算:用来求n在二进制下最低的位
lowbit(n)=n&(~n+1)=n&(-n)
应用于树状数组
另:结合hash可以求出所有1的位
代码如下
#include<iostream>
#include<cstring>
using namespace std;
int h[1<<20];
int n,i;
int main()
{
for (i=0;i<=20;i++)
{
h[1<<i]=i;
}
while(cin>>n)
{
if (n!=0) cout<<h[n&(-n)];
n=n^n&(-n);
while (n!=0)
{
cout<<" "<<h[n&(-n)];
n=n^n&(-n);
}
cout<<endl;
}
return 0;
}
另外 2^i 在0<=i<=35时 有 其模37余数为1——36各不相等 从而可以简单化上述hash
#include<iostream>
#include<cstring>
using namespace std;
int h[100];
long long n,i;
int main()
{
for (i=0;i<=35;i++)
{
h[(1ll<<i)%37]=i;
}
while(cin>>n)
{
if (n!=0) cout<<h[(n&(-n))%37];
n=n^n&(-n);
while (n!=0)
{
cout<<" "<<h[(n&(-n))%37];
n=n^n&(-n);
}
cout<<endl;
}
return 0;
}