给出N个[0, 65535]的整数,编程支持以下操作:
修改操作:C d,所有数增加d。如果超过65535,把结果模65536。0 <= d <= 65535
查询操作:Q i,统计有多少整数的第i位非0,换句话说,有多少个整数与2i的“按位与”操作值为正。0 <= i <= 15
输出所有查询操作的统计值。
输入格式:
第一行为两个正整数N和M,即整数的个数和操作的个数,第二行包含N个[0,65535]的整数。以下M行为各操作,格式如题所述。
输出格式:
输出M个数,即所有Q操作的统计值。
样例输入:
3 5 1 2 4 Q 1 Q 2 C 1 Q 1 Q 2
样例输出:
1 1 1 2
数据范围:
数据 1 2 3 4 5 6 7 8 9 10
N 3 10 100 103 104 2*104 5*104 105 105 105
M 3 10 100 103 104 2*104 5*104 5*104 105 2*105
时间限制:
1000
空间限制:
512000
烧脑题,我们对于一个数,判断其第i位是零,当且仅当该数除以2^(i+1)次方大于等于2^i所以就转化为了求一段区间的和,加上一个数相当于该数组向右移动一段距离,这样子不如用一个头指针标志一下起始点,然后转前缀和这样就能在O(1)完成所有询问。
预处理复杂度O(n*16+65535*16) 这样就A了
下次遇到这种区间扔进去就不改的往头指针前缀和,转化区间和来解决。
#include<bits/stdc++.h>
using
namespace
std;
int
a[17][1000001],len[17],l[17],siz[17],digit,n,m;
string type;
int
doit(
int
no,
int
st,
int
en)
{
if
(st<=en)
{
if
(st==0)
return
a[no][en];
else
return
a[no][en]-a[no][st-1];
}
else
return
a[no][siz[no]-1]+a[no][en]-a[no][st-1];
}
int
main()
{
cin>>n>>m;
for
(
int
i=0;i<16;i++)
{
siz[i]=2<<i;
l[i]=1<<i;
len[i]=l[i]-1;
}
for
(
int
i=1;i<=n;i++)
{
cin>>digit;
for
(
int
j=0;j<16;j++)
{
a[j][digit%siz[j]]++;
}
}
for
(
int
j=0;j<16;j++)
for
(
int
i=1;i<siz[j];i++)
{
a[j][i]+=a[j][i-1];
}
while
(m--)
{
cin>>type>>digit;
if
(type==
"C"
)
{
for
(
int
i=0;i<16;i++)
{
l[i]-=digit%siz[i];
if
(l[i]<0) l[i]+=siz[i];
}
}
else
cout<<doit(digit,l[digit],(l[digit]+len[digit])%siz[digit])<<endl;
}
}