目录
位查询
要求:
总时间限制: 5000ms
内存限制: 65536kB
描述:
给出N个范围在[0, 65535]的整数,编程支持以下的操作:
(1)修改操作:C d,所有的数都增加d。如果超过65535,把结果模65536。 0 <= d <= 65535
(2)查询操作:Q i,统计在N个正整数中有多少个整数其对应的二进制形式的第i位二进制位为非0。0 <= i <= 15。并且最低位i为0。最后,输出所有查询操作的统计值。
输入:
输入的第一行为两个正整数N,M,其中N为操作的整数的个数,而M为具体有多少个操作。
输入的第二行为N个正整数,为进行操作的N个正整数。
下面有M行,分别表示M个操作。
N<=100000,M<=200000
输出:
输出所有查询操作Q的统计值,每一个查询操作统计结果输出为一行。
样例输入:
3 5 1 2 4 Q 1 Q 2 C 1 Q 1 Q 2
样例输出:
1 1 2 1
思路分析:
这个题目总体来说还是比较简单,在输入中有这样的要求 ,看似十分严格,但实际的测试数据好像并没有这么大,所以最朴素的二层循环就可以解决这个问题。
唯一需要注意的点就是在C d的操作中,题目中提到如果超过65536就取模,如果我们采取模运算,实际上是存在不必要的模运算消耗。
一个很好的替代品是unsigned short类型,这里需要对不同数据类型有所了解:在x86-64系统中,char型占用1个字节(=8 bits),short型占用2个字节,int型占用4个字节,long型占用8个字节,由于,所以2字节的short类型可以在溢出的情况下自动取模,又因为不考虑负数,所以unsigned short成为最理想的选择.
位查询有两种方式,一种是将原数右移i位再位与1((num>>i)&1),一种是原数位与左移i位的1(num&(1<<i)),这两种方式都可以,我在代码中采用的是后者.
最终代码:
#include<iostream>
using namespace std;
static int cnt=0;
int main(){
int N,M;
scanf("%d%d",&N,&M);
unsigned short num[N];
for(int i=0;i<N;i++){
scanf("%hd",&num[i]);
}
for(int i=0;i<M;i++){
getchar();//读掉行末的回车
char ch=getchar();
unsigned short tmp;
scanf("%hd",&tmp);
if(ch=='C'){
for(int j=0;j<N;j++){
num[j]+=tmp;
}
}
else{
cnt=0;
for(int j=0;j<N;j++){
if(num[j]&(1<<tmp)) cnt++;
}
printf("%d\n",cnt);
}
}
}