文章目录
- 按位贪心
- 1、毒瘤xor
- 2、兔子的区间密码
- 3、起床困难综合症
按位贪心
1、毒瘤xor
NC18979 毒瘤xor
题目描述
小a有N个数a1, a2, …, aN,给出q个询问,每次询问给出区间[L, R],现在请你找到一个数X,使得
1、0⩽X<231
2、 ∑ i = L R \sum_{i=L}^R ∑i=LRX⊕a[i]最大,⊕表示异或操作(不懂的请自行百度)
输入描述:
第一行一个整数N,表示序列的长度
第二行N个整数,表示序列内的元素
第三行一个整数q,表示询问的个数
接下来q行,每行两个整数[L, R],表示询问的区间
输出描述:
输出q行,每行一个整数表示答案
若有多组可行解,请输出较小的解
示例1
输入
5
4 78 12 1 3
3
2 5
1 4
3 3
输出
2147483632
2147483635
2147483635
备注:
对于30%的数据,n , q ≤ 10
对于60%的数据,n , q ≤ 1000
对于100%的数据,n, q ≤ 105
保证ai < 231
解题思路:
1、按二进制位来分析问题,统计区间中每一位1的数量,那么0的数量可以间接求出来,如果0的数量比1的数量多,那么该位就应该是1,否则就应该是0,下面来解释样例
2、2到5有4个数78、12、1、3,二进制分别如下
78 :0100 1110
12 :0000 1100
1 :0000 0001
3 :0000 0011
前面24位全0省略了,那么一共有4个数,那么当0的个数大于等于3时该位是1,那么X应该是111 1111 1111 1111 1111 1111 1111 0000,即2147483632
代码:
#include<bits/stdc++.h>
using namespace std;
int main(){
int n;
cin>>n;
int a[100010]={0};
int b[100010][32]={0};
for(int i=1;i<=n;i++){
cin>>a[i];
for(int j=0;j<31;j++){//统计每一位的1的个数,进行前缀和
b[i][j]+=b[i-1][j]+((a[i]>>j)&1);
}
}
int m;
cin>>m;
for(int i=0;i<m;i++){
int l,r;
cin>>l>>r;
int ans=0;
for(int j=0;j<31;j++){
//使用差分快速查询每一位区间的和,如果1的数量小于0的数量,那么该位就为0
if((b[r][j]-b[l-1][j])*2<(r-l+1)){
ans+=(1<<j);
}
}
cout<<ans<<endl;
}
}
2、兔子的区间密码
NC20860 兔子的区间密码
题目描述
有一只可爱的兔子被困在了密室了,密室里有两个数字,还有一行字:
只有解开密码,才能够出去。
可爱的兔子摸索了好久,发现密室里的两个数字是表示的是一个区间[L,R]
而密码是这个区间中任意选择两个(可以相同的)整数后异或的最大值。
比如给了区间[2,5] 那么就有2 3 4 5这些数,其中 2 xor 5=7最大 所以密码就是7。
兔子立马解开了密室的门,发现门外还是一个门,而且数字越来越大,兔子没有办法了,所以来求助你。
提示:异或指在二进制下一位位比较,相同则 0 不同则 1
例如2=(010)2,5=(101)2
所以2 xor 5=(111)2=7
输入描述:
第一行一个数 T,表示数据组数。
接下来 T 行,每行两个数 L,R, 表示区间[L,R]。
输出描述:
输出共T行每行一个整数,表示[L,R]的密码。
示例1
输入
5
1 10
2 3
3 4
5 5
2 5
输出
15
1
7
0
7
备注:
对于30%的数据
1 ≤ T ≤ 10
0 ≤ L ≤ R ≤ 100
对于另外10%的数据
L=R
对于70%的数据
1 ≤ T ≤ 10
0 ≤ L ≤ R ≤ 50000
对于100%的数据
1 ≤ T ≤ 10000
0 ≤ L ≤ R ≤ 1018
(对于100%的数据) 输入数据较大,请使用快速读入。
解题思路:
下面来解释样例
1、1和10,1的二进制是0000 0001,10的二进制是0000 1010,找到第一位不同的,那就是第5位,那么选择的第一个数就是0000 1000,第二个数是0000 0111,答案是0000 1111,即15
2、2和3,2的二进制是0000 0010,3的二进制是0000 0011,找到第一位不同的,那就是第8位,那么选择的第一个数就是0000 0011,第二个数是0000 0010,答案是0000 0001,即1
3、3和4,3的二进制是0000 0011,4的二进制是0000 0100,找到第一位不同的,那就是第6位,那么选择的第一个数就是0000 0100,第二个数是0000 0011,答案是0000 0111,即7
4、相信您看出来规律了吧!答案一定是2的幂次方-1。
代码:
#include<bits/stdc++.h>
using namespace std;
int main(){
int t;
cin>>t;
while(t--){
long long l,r;
long long ans=0;
cin>>l>>r;
for(int i=62;i>=0;i--){
if(((l>>i)&1)!=((r>>i)&1)){//出现了不同的位
ans=((1ll<<(i+1))-1);//该位后全1
break;
}
}
cout<<ans<<endl;
}
}
3、起床困难综合症
NC17857 起床困难综合症
题目描述
21 世纪,许多人得了一种奇怪的病:起床困难综合症,其临床表现为:起床难,起床后精神不佳。作为一名青春阳光好少年,atm 一直坚持与起床困难综合症作斗争。通过研究相关文献,他找到了该病的发病原因:在深邃的太平洋海底中,出现了一条名为 drd 的巨龙,它掌握着睡眠之精髓,能随意延长大家的睡眠时间。正是由于 drd 的活动,起床困难综合症愈演愈烈,以惊人的速度在世界上传播。为了彻底消灭这种病,atm 决定前往海底,消灭这条恶龙。
历经千辛万苦,atm 终于来到了 drd 所在的地方,准备与其展开艰苦卓绝的战斗。drd 有着十分特殊的技能,他的防御战线能够使用一定的运算来改变他受到的伤害。具体说来,drd 的防御战线由 𝑛 扇防御门组成。每扇防御门包括一个运算 op 和一个参数 𝑡,其中运算一定是 OR,XOR,AND 中的一种,参数则一定为非负整数。如果还未通过防御门时攻击力为 𝑥 ,则其通过这扇防御门后攻击力将变为 𝑥 op 𝑡 。最终drd 受到的伤害为对方初始攻击力 𝑥 依次经过所有 𝒏 扇防御门后转变得到的攻击力。
由于atm 水平有限,他的初始攻击力只能为 0 到 𝑚 之间的一个整数(即他的初始攻击力只能在 0, 1, … , 𝑚 中任选,但在通过防御门之后的攻击力不受 𝑚 的限制)。为了节省体力,他希望通过选择合适的初始攻击力使得他的攻击能让 drd受到最大的伤害,请你帮他计算一下,他的一次攻击最多能使 drd 受到多少伤害。
输入描述:
第 1 行包含2 个整数,依次为 𝑛, 𝑚 ,表示drd 有 𝑛 扇防御门,atm 的初始攻击力为 0 到 𝑚 之间的整数。
接下来 𝑛 行,依次表示每一扇防御门。每行包括一个字符串 op 和一个非负整数 𝑡,两者由一个空格隔开,且 op 在前, 𝑡 在后,op 表示该防御门所对应的操作,𝑡 表示对应的参数。
输出描述:
输出一行一个整数,表示atm 的一次攻击最多使 drd 受到多少伤害。
示例1
输入
3 10
AND 5
OR 6
XOR 7
输出
1
说明
atm 可以选择的初始攻击力为 0,1, … ,10。
假设初始攻击力为 4,最终攻击力经过了如下计算
4 AND 5 = 4
4 OR 6 = 6
6 XOR 7 = 1
类似的,我们可以计算出初始攻击力为 1,3,5,7,9 时最终攻击力为 0,初始攻击力为 0,2,4,6,8,10 时最终攻击力为 1,因此atm 的一次攻击最多使 drd 受到的伤害值为 1。
备注:
解题思路:
1、按位贪心,越高位比重越高,我们应该从最高位开始,向最低位逐步分析问题
2、因为按位与、按位或、按位异或都是不会产生进位的,所以我们可以按位独立去考虑问题,某位不是1就是0,那么我们可以让那一位是1和0,查看结果再做判断,例如某一位是1时,经过一系列操作变成了0,是0时,经过一系列操作变成了1,那么就让该位是0是最合适的
3、从高位到低位贪心,如果该位能变1,那么在符合要求的情况下一定要令其为1
代码:
#include<bits/stdc++.h>
using namespace std;
int main(){
int n,m;
cin>>n>>m;
int t=0;
int ans=0;
int ma=(1ll<<31)-1;//全1
int mi=0;//全0
for(int i=0;i<n;i++){
string s;
int val;
cin>>s>>val;
if(s=="AND"){
ma&=val;
mi&=val;
}else if(s=="OR"){
ma|=val;
mi|=val;
}else{
ma^=val;
mi^=val;
}
}
for(int i=30;i>=0;i--){
if((mi>>i)&1){//如果该位是0,最后变1了,那么就让这位是0
ans+=(1<<i);
continue;
}else if(((ma>>i)&1)&&t+(1<<i)<=m){//如果该位是1,最后还是1,那么在符合条件的情况下让该位是1
t+=(1<<i);
ans+=(1<<i);
}
}
cout<<ans<<endl;
}
是不是很简单呢?
刚接触肯定会觉得难,多些做题多些用,熟悉了就容易了,兄弟萌,加油!!!
文章尚有不足,感谢您的指正
感谢观看,点个赞吧