0x01位运算

与:and ,&
或:or , |
非:not ,~
异或:xor , ^
m位2进制数:从右往左依次为0~m-1位

补码
移位运算

左移:1<<n = 2n ; n<<1 = 2n
(低位以0填充,高位越界舍弃)
算术右移:n>>1 = (n/2.0)向下取整
PS:与除法区别,整数除法是向0取整
(在二进制补码表示下把数字同时向右移动,高位以符号位填充,低位越界舍弃)
逻辑右移:高位以0填充,低位越界舍弃
一般编译器默认算术右移

应用:

快速幂:
int quickpow(int a,int b,int p)
{
	int res=1%p;
	while(b)
	{
		if(b&1) res=(long long)res*a%p;
		a=(long long)a*a%p;
		b>>=1;
	}
	return res;
}
快速乘:

O(log2b)

int quickmul(long long a,long long b,long long p)
{
	long long res=1%p;
	while(b)
	{
		if(b&1) res=(res+a)%p;
		a=a*2%p;
		b>>=1;
	}
	return res;
}

O(1)

	typedef unsigned long long ull;
	ull quickmul(ull a,ull b,ull p)
	{
		a%=p;b%=p;
		ull c=(long double)a*b/p;
		ull x=a*b,y=c*p;
		long long res=(long long)(x%p)-(long long)(y%p);
		if(res<0) res+=p;
		return res;
	}
	
二进制状态压缩

用一个m位二进制整数表示并存储一个长为m的bool数组
相关二进制位运算操作

操作运算
取出整数n在二进制表示下第k位(n>>k)&1
取出整数n在二进制表示下后k位(0~k-1)n&( (1<<k)-1)
把整数n在二进制表示下第k位取反n xor (1<<k)
对整数n在二进制表示下第k位赋值1n I (1<<k)
对整数n在二进制表示下第k位赋值0n&(~(1<<k))

例题
最短Hamilton路径
在这里插入图片描述分析:
Hamilton 路径要求从 0 到 n−1 不重不漏地经过每个点恰好一次。可用一个n位二进制整数表示某路径各点访问状态,每一位表示第i个点是否被访问过,访问过置1,未被访问过置0,故Hamilton 路径即为(1<<n -1),加上在任意时刻当前所处的位置,于是使用F[i][j]表示:路径中各点是否被经过的状态所对应的二进制数为i,且当前处于点j时的最短路径。
为求最短路径,将F数组中所有数初始化一个最大值

  • 起点:F[1][0]=0:只经过起点且当前仍位于起点的最短路长度为0
  • 终点:F[(1<<n)-1][n-1]:经过所有点且处于终点的最短路
  • 任意时刻状态:若((i>>j)&1)=1即当前路径经过j点,且处于j点,必然是刚刚经过j点,枚举其上一时刻的状态:j点必然未被访问,j位为0,假设上一时刻经过k点再从k->j,
    即F[i][j]=F[i xor (1<<j)][k]+weight[k][j],在枚举过程中找到F[i][j]最小值

F[i][j]=min{F[i xor (1<<j)][k]+weight[k][j],F[i][j]}

代码:

#include<bits/stdc++.h>
using namespace std;
int F[1<<20][20];
int weight[20][20];
int main()
{
    int n;cin>>n;
    for(int i=0;i<n;++i){
        for(int j=0;j<n;++j)
        {
            cin>>weight[i][j];
        }
    }
    memset(F,0x3f,sizeof(F));
    F[1][0]=0;
    for(int i=1;i<(1<<n);++i)
    {
        for(int j=0;j<n;++j)
        {
            if((i>>j)&1)
            {
                for(int k=0;k<n;++k)
                {
                    if((i^(1<<j))>>k&1)
                    F[i][j]=min(F[i][j],F[(i^(1<<j))][k]+weight[k][j]);
                }
            }
        }
    }
    cout << F[(1<<n)-1][n-1];
}

起床困难综合征
在这里插入图片描述分析:
本题简化题意后即为:选择0-m之间一个整数,对其进行给定n次的位运算,使结果res最大
首先:位运算的主要特点是在二进制表示下不进位,也即在二进制位表示下的整数X的每一位分别与给定给定数字的该位进行位运算,各位运算之间互不影响,因此我们枚举整数X的每一位两种可能0或1,对每种情况进行n次二进制运算,选出符合题目条件的最优解。
为使结果最大化我们选择从最高位开始枚举,因为m的数据范围,所以枚举30位二进制,29~0
对于每一次枚举:
计算该位取0和1两种情况下,经过n次位运算的结果
当且仅当该位置1时所得整数小于等于m且取1运算优于取0运算时,该位置1,res自加上该位取1运算结果
否则该位置0,res自加上该位取0运算结果

代码:

#include<bits/stdc++.h>
using namespace std;
pair<string,int>op[100005];
int n,m;
int cal(int bit,int now)
{
    for(int i=0;i<n;++i)
    {
        int t=(op[i].second>>bit)&1;
        if(op[i].first=="AND") now&=t;
        else if(op[i].first=="OR") now|=t;
        else now^=t;
    }
    return now;
}
int main()
{
    cin>>n>>m;
    for(int i=0;i<n;++i)
    {
        string s;int x;
        cin>>s>>x;
        op[i]=make_pair(s,x);
    }
    int res=0,ans=0;
    for(int i=29;i>=0;--i)
    {
        int res1=cal(i,1),res0=cal(i,0);
        if(ans+(1<<i)<=m&&res1>res0)
        {
            ans+=(1<<i);res+=(res1<<i);
        }
        else res+=(res0<<i);
    }
    cout<<res<<endl;
}

lowbit运算
lowbit(n)定义为非负整数n在二进制表示下“最低位的1及其后边所有的0"构成的数值。
lowbit(n)=n&(~n+1)=n&(-n)

lowbit运算配合Hash可以找出整数二进制下所有是1的位

int H[37];
for(int i=0;i<36;++i) H[(1ll<<i)%37]=i;
while(cin>>n)
{
	while(n>0)
	{
		cout<<H[(n&-n)%37]<<' ' ;
		n-=n&-n;
	}
	cout<<endl;
}

一些相关的内置函数(比赛中不建议使用)
int _builtin_ctz(unsigned int x)
int _builtin_ctzll(unsigned long long x)
返回x的二进制表示下最低位的1后面有多少个0

int _builtin_popcount(unsigned int x)
int _builtin_popcountll(unsigned long long x)
返回x的二进制表示下有多少位为1

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值