复习:位运算
为了打篮球杯而捡起来之前学的oi
TA?那是什么东西,能吃吗?
/其实是感觉这行现状一般前景惨淡想着我还年轻趁早跑路比较好
本篇大概是位运算专题,之后以位运算为主的题目基本都会放在这里吧 主要以题目为主,大概不会出单独章节讲知识
1.求a^b%p,ab均小于1e9
直接一个个乘的话时间复杂度是O(b)
考虑位运算,任何一个自然数可以用多个2的n次方相加得到 例如10011,可以表示为
如果b可以转化为一个k位的二进制数,然后a^b就可以表示为
每次都进行b>>1的操作,并进行b&1的判断。由于&是按位进行,所以可以判断最后一位是否是1.若是1则更新答案。并且每次循环都需要更新要乘的a值,比如如果是1011,要乘的a值则分别为a,a^2 以及a^8
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll a,b,p;//求a的b次方对p取模
ll ans=1;
int main()
{
cin>>a>>b>>p;
for(;b;b>>=1)
{
if(b&1) ans=ans*a%p;
a=a*a%p;//如果末位是0则不更新ans。
}
printf("%ld",ans);
return 0;
}
2.64位整除乘法,求a*b%p
和上题思路类似,由幂变成了相乘,原本更新ans式子里的相乘变成相加,ans初值改为0即可
#include<bits/stdc++.h>
#define ll long long
using namespace std;
ll a,b,p;
ll ans=0;
int main()
{
scanf("%ld%ld%ld",&a,&b,&p);
for(;b;b>>=1)
{
if(b&1) ans=ans+a%p;
a=a+a%p;//如果末位是0则不更新ans。
}
printf("%ld",ans);
return 0;
}
CF round941 D(div2)
题目大意:给定一个n和k(k<= n ),让你构造一个长度不大于25的数列,使得数列的任一子序列的和不能等于k。同时对于所有的v < = n并且v!=k,都有一个子序列使得子序列的和等于v。对于每个询问输出数列,
首先抛开k不谈,我们想构造一个可以组合出所有数字的序列
即1,2,4,8,16,32.......
我们的工作就是要在这个序列的基础上进行改动使其不能得到k,其他不变。
对于k,我们求出最大的i使得2的i次方小于等于k。
如果我们想构造出k,只需要这个序列的前i个数。换言之,如果我们去掉第i个,就不能构造k。
然后我们就能构造出来1到(2^i)-1的所有数。
但这样不行,从这部分到k的差距我们需要补齐,于是我们往序列中加一个数:k-((2^i )-1)-1,化简后是k-2^i。这样我们就能用它和前面的数构造出所有1到k-1。
但这样还是不够。由于我们去掉了2^i ,所以如果我们要求一个数t+2^i 就会出现问题。所以我们要想办法把2^i 这个空缺补上。我们已知构造比k大的数是绝对安全的,所以我们尝试构造k+2^i ,但这样又出现一个问题。
假设我们要构造一个数x使其能够替代2^i 的功效。假设x为t+2^i 。我们会在什么时候缺少t+2^i 呢?由于我们只少了一个2^i ,所以我们一定是能求出t,然后需要一个2^i ,此时我们直接用x来替代整个t+2^i 。也就是说如果我们求不出来t的话是没用的。但我们求不出k,所以我们不能用k+2^i ,然后我们用k+1+2^i ,但其实我们这时候序列里还没有k+1,于是我们设k+1,有k+1后我们能求出2k以内除了k所有的数字。k+1+2^i 最大值是当k==2^i 的时候值是2k+1因此2k+1以及之后的数字都可以用到2^i 。我们只要保证在2^(i+1) 前能求出2^(i+1) -1内的所有数字就可保证能求出所有数字。已知2^i <= k,那么2k一定大于等于2^(i+1) ,所以我们能求出所有数字。
综上所述,我们在去掉2^i的情况下往序列中加入三个数字,就是正确序列。
代码比较简单,思路有点要命
#include<bits/stdc++.h>
using namespace std;
const int maxn=1e6+10;
int main()
{
int t;
cin>>t;
while(t--)
{
int n,k;
cin>>n>>k;
n=maxn;
int t=1;
int i;
for(i=0;i<=32;i++)
{
if(t>k) break;
t<<=1;
}
t>>=1;i-=1;
//2--k+t
cout<<25<<endl;
cout<<k-t<<' '<<k+t+1<<' '<<k+1<<' ';
int t2=1;
for(int j=0;j<=22;j++)
{
if(j==i)
{
t2<<=1;
continue;
}
cout<<t2<<' ';
t2<<=1;
}
cout<<endl;
}
return 0;
}
牛客:毒瘤xor
我们首先来想,如果不考虑l和r的话,想要使得数组中的所有数与x的异或和最大该如何做。
将所有数字转换为二进制,不难想到,对于每一位来说,这一位上的0如果比1多,那么x的这一位就应该取1(异或后得到的1更多),反之应该取0,如果0和1一样多取0(数字尽量小)
所以我们需要统计出每一位的1的数量,用n减去1的数量就是0的数量,进行比较即可
回到原题,我们需要求出l到r区间内的每一位1的数量,不难想到前缀和
因此开一个大小为[maxn][32]的二维数组,sum[i][j]记录的是第i之前所有数第j位1的总个数,这样就能计算出l到r每一位的个数
判断0的数量和1的数量时总个数为(r-l+1)而非n要注意
还有这题卡空间,多开一个大数组就过不了了
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,q,cnt=0;
const int maxn=1e5+10;
int sum[maxn][32];
signed main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
int x,cnt=0;
scanf("%lld",&x);
for(int j=1<<30;j>0;j>>=1)
{
cnt++;
if(x&j) sum[i][cnt]++;
}
}
for(int i=1;i<=n;i++)
for(int j=1;j<=31;j++)
sum[i][j]=sum[i-1][j]+sum[i][j];
cin>>q;
for(int i=1;i<=q;i++)
{
int l,r;
scanf("%lld%lld",&l,&r);
int ans=0;
for(int j=1;j<=31;j++)
{
int t=sum[r][j]-sum[l-1][j];
// cout<<t<<' ';
if(t<(r-l+1)-t) ans+=1<<(31-j);
}
// cout<<endl;
cout<<ans<<endl;
}
return 0;
}
牛客:兔子的区间密码
意思就是l到r之间随便取两个数令他们的异或值最大,求这个最大值
当两个数二进制位数不一样时,比如10111和1001,一个五位一个四位,我们显然可以选择10000和1111来异或成为11111.
当l和r二进制位数相同时,前面相同的部分异或值都必然为0,直到遇到不同时,我们可以继续按照上面的方法处理
因此只需要找到第一次出现不同的地方然后计算答案即可,假如是第i位不同,答案就是((long long)1<<i )-1,别忘了位运算优先级较低需要加括号,1需要加longlong
#include<bits/stdc++.h>
#define int long long
#define ll long long
using namespace std;
int n;
signed main()
{
cin>>n;
for(int i=1;i<=n;i++)
{
int l,r,cnt=0;
scanf("%lld%lld",&l,&r);
int j;
for(j=63;j>=0;j--)
{
if((l>>j)!=(r>>j)) break;
}
//cout<<j<<endl;
cout<<((1ll<<(j+1))-1)<<endl;
}
return 0;
}
牛客:[NOI2014]起床困难综合症
让你选择一个数,最大为m,使得令其进行一系列位运算后答案最大
我想的做法是对于每一位都用1和0分别运算一遍,看哪个有结果,答案和我思路类似不过更简洁
就是一开始用两个变量分别为0和-1(转换为二进制分别为0000...和1111....)然后在输入时就直接进行位运算,最后看两个变量的每一位的值,如果为1就说明可以取,因为不能超过m所以要尽量小就优先取初始为0的。
当初始为0不行而初始为1可以时就看是否超过范围(用一个变量t存,每次第j位取1时令t加上1 <<(j-1)和最大值m比较 ),如果不超过范围就令最终答案加上1<<(j-1)。
#include<bits/stdc++.h>
#define int long long
using namespace std;
int n,m,cnt=0;
const int maxn=1e5+10;
int a[100];
signed main()
{
cin>>n>>m;
int ans0=0,ans1=-1;
for(int i=1;i<=n;i++)
{
string s;
int t;
cin>>s>>t;
if(s[0]=='A') ans0=ans0&t,ans1=ans1&t;
if(s[0]=='O') ans0=ans0|t,ans1=ans1|t;
if(s[0]=='X') ans0=ans0^t,ans1=ans1^t;
}
int ans=0,t=0;
for(int i=(1<<30);i>0;i>>=1)
{
// cout<<i<<endl;
if(ans0&i) ans+=i;
else if((ans1&i)&&(t+i<=m)) ans+=i,t+=i;
}
cout<<ans;
return 0;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】