【位运算】
【位运算】
(1)按位取优化:把n^2优化为64n
(2)数学:异或(xor) 不进位加法
基本操作
~
取反
运算律
计算完等于自身的
A&A=A
A|A=A
A|0=A
A^0=A
计算完等于0的
A&0=0
A^A=0
取位操作
注意二进制都是从第0位开始!!!
(n>>k)&1
取第k位
x&((1<<k)-1)
取末k位
按位变化
n^(1<<k)
第k位取反
n|(1<<k)
第k位赋1
n&(~(1<<k))
第k位赋0
x|(1<<k)
右数第k位变1
x|((1<<k)-1)
右数前k-1位都变1
乘除法
a<<1
a*2
a>>1
a/2
1<<a
2^a
交换两整数
swap(int &a,int &b){
a^=b;
b^=a;
a^=b;
}
判断奇偶
提取最低位即可
统计二进制中1的个数
int cnt=0;
while(x){
x=x&(x-1);//遇到一个1消一次1
cnt++;
}
【题目整理】
(构造)Trip to the Olympiad
https://codeforces.com/problemset/problem/2057/C
/*
【思路】
最后的和都是二进制的每一位*2
对于二进制每一位:构造0 1 0 或者 1 0 1
【构造题不要想太复杂!】
直接取l到r中最大能被2^k整除的数 然后取这个数-1 然后随便取一个除了这两个数之外的数
【位运算要熟悉!!!】
*/
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef pair<int,int> PII;
typedef long long ll;
ll abss(ll a){return a>0?a:-a;}
ll max_(ll a,ll b){return a>b?a:b;}
ll min_(ll a,ll b){return a<b?a:b;}
bool cmpll(ll a,ll b){return a>b;}
int t;
void solve(){
ll l,r;
cin>>l>>r;
if(l>r) swap(l,r);
int k=31-__builtin_clz(l^r);//__builtin_clz(l^r)求前导0的个数 k求l到r区间中最高到多少位
//逆向思维:我不直接求整除 我求整除-1 -> 很方便的能用位运算操作
int a=l|((1<<k)-1);//从(最高位-1)位每一位都置1(a>=l)
int b=a+1;//最高位为1 其他都是0:最大能被2^k整除的数
int c=a==l?r:l;
cout<<a<<" "<<b<<" "<<c<<endl;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>t;
while(t--){
solve();
}
return 0;
}
2^20
https://ac.nowcoder.com/acm/contest/66877/D
注意在二进制的范围内讨论2^20的倍数的含义
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef pair<int,int> PII;
typedef long long ll;
ll abss(ll a){return a>0?a:-a;}
ll max_(ll a,ll b){return a>b?a:b;}
ll min_(ll a,ll b){return a<b?a:b;}
bool cmpll(ll a,ll b){return a>b;}
const int N=100010;
const int mod=1<<20;
int t;
/*
一直*2 乘20次就能为2^20的倍数->答案不会大于20
统计最后几位为0->20减去
->【策略】枚举加的次数,统计乘的次数
*/
void solve(){
ll n;
cin>>n;
n%=mod;
int res=20;
//注意需要枚举!!!不能贪心思想只加一次
for(int i=0;i<=20;i++){
int val=(n+i)%mod;
//已经是倍数了就不需要乘了 直接统计答案
if(!val) res=min(res,i);
else{
//乘法:二进制补0
int num=0;
while(!(val&1)){
val/=2;
num++;
}
res=min(res,20-num+i);
}
}
cout<<res<<endl;
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>t;
while(t--){
solve();
}
return 0;
}
异或与位移【公式简化+逆运算推公式】
https://ac.nowcoder.com/acm/contest/100007/C
公式
公式推导
->向右/向左移a[i]位->向下取整直接舍去(整数操作)
->取模限定范围直接舍去
->异或运算的性质
代码
#include<bits/stdc++.h>
using namespace std;
const int N=10010;
int n,m,k;
int a[25];
string ss;
int main(){
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
while(m--){
cin>>ss;
//bitset优化
bitset<N> s;
int l=ss.size();
for(int i=0;i<l;i++){
s[i]=ss[l-1-i]-'0';
}
//求逆运算
for(int i=n;i>=1;i--){
bitset<N> t=s;
//讨论a[i]正负
if(a[i]>0){
//a[i]为正->相当于左移a[i]位->(逆运算)往右找值
for(int j=a[i];j<k;j++){
//<2的k次方->取不到k
//遍历a[i]到k->修正左边的值
t[j]=t[j]^t[j-a[i]];
}
}
else{
//a[i]为负->相当于右移a[i]位->(逆运算)往左找值
//遍历k-1(取不到k所以要-1)+a[i](会减)到0->修正右边的值
for(int j=k-1+a[i];j>=0;j--){
t[j]=t[j]^t[j-a[i]];
}
}
//每完成一次更新还原t到s
s=t;
}
//从高位输出到低位->k-1到0->去掉前缀0
int flag=0;
for(int i=k-1;i>=0;i--){
if(s[i]) flag=1;
if(flag) cout<<s[i];
}
if(!flag) printf("0");
printf("\n");
}
return 0;
}
(构造)小苯的数组构造
https://ac.nowcoder.com/acm/contest/105623/E
注意如果要空出来一位:请用skip轮每一个数字->避免制造0
#include<bits/stdc++.h>
using namespace std;
#define endl '\n'
typedef pair<int,int> PII;
typedef long long ll;
ll abss(ll a){return a>0?a:-a;}
ll max_(ll a,ll b){return a>b?a:b;}
ll min_(ll a,ll b){return a<b?a:b;}
bool cmpll(ll a,ll b){return a>b;}
int t;
int n;
ll x,y;
void solve(){
cin>>n>>x>>y;
if(n==1){
if(x==y){
cout<<"YES"<<endl;
cout<<x<<endl;
}
else{
cout<<"NO"<<endl;
}
}
else{
//把每一位都提出来:确定x y
vector<int> ans(n+1,0);
int skip=1;//跳掉哪一个数字:用skip累加
for(int i=0;i<=31;i++){
int t1=(x>>i)&1,t2=(y>>i)&1;
if(t1==1 && t2==0){//当前这一位有偶数个1
if(n%2==0){
for(int j=1;j<=n;j++){
ans[j]=ans[j]|(1<<i);
}
}
else{
for(int j=1;j<=n;j++){
if(skip==j) continue;
ans[j]=ans[j]|(1<<i);
}
skip=min(skip+1,n);
}
}
else if(t1==0 && t2==1){
cout<<"NO"<<endl;
return;
}
else if(t1==1 && t2==1){//当前这一位有奇数个1
if(n%2){
for(int j=1;j<=n;j++){
ans[j]=ans[j]|(1<<i);
}
}
else{
for(int j=1;j<=n;j++){
if(skip==j) continue;
ans[j]=ans[j]|(1<<i);
}
skip=min(skip+1,n);
}
}
}
int xx=0,yy=0;
for(int i=1;i<=n;i++){
if(ans[i]<=0){
cout<<"NO"<<endl;
return;
}
xx=xx|ans[i];
yy=yy^ans[i];
}
if(xx!=x || yy!=y){
cout<<"NO"<<endl;
return;
}
cout<<"YES"<<endl;
for(int i=1;i<=n;i++) cout<<ans[i]<<" ";
cout<<endl;
}
}
signed main(){
ios::sync_with_stdio(0);
cin.tie(0);
cout.tie(0);
cin>>t;
while(t--) solve();
return 0;
}
(贪心)汉堡猪猪分糖果
https://ac.nowcoder.com/acm/contest/112544/F
一般都是从高位往低位贪心
注意100000-1=011111
->低位之和高不过高位
题目大意
n个糖分给m个小朋友,求小朋友获得糖的按位与最大值
思路
代码
i64 n,m;
void solve(){
cin>>n>>m;
i64 ans=0;
for(i64 i=30;i>=0;i--){
i64 v=m*(1LL<<i);
if(n>=v){
n-=v;
ans|=(1LL<<i);
}
else{
if(n>=(v-m)){
i64 k=(n-(v-m)+(1LL<<i)-1LL)/(1LL<<i);
i64 res=k*(1LL<<i);
n-=res;
}
}
}
cout<<ans<<endl;
}