Description
Solution
60%
因为区间固定,所以并不需要线段树
维护一个数组,记录每个二进制位是1的有多少个。
然后修改就暴力改,询问就直接询问就好
100%
继续按照上面的思路,如果 x>0 怎么办?
还是按照每一位来,我们只需要看看减去了这个数每一位还有多少个1
可以每一位维护一个树状数组,以原数组值作为下标,第
i
位维护
那么这一位是1就是 2i ~ 2i+1−1 区间和。
然后这一区间的左边界和右边界向左移 x ,小于0的就移回右边(因为在模下)
修改就在树状数组上改
注意维护的时候下标全部加1,避免出现0。
复杂度
Code
#include<cstdio>
#include<cstdlib>
#include<cmath>
#include<cstring>
#include<algorithm>
#include<iostream>
#define fo(i,a,b) for(i=a;i<=b;i++)
#define fod(i,a,b) for(i=a;i>=b;i--)
using namespace std;
int c[21][2150005],a[100005],b[21],n,m;
int lowbit(int x)
{
return (x&-x);
}
int gets(int p,int k)
{
int s=0;
while (k>0)
{
s+=c[p][k];
k-=lowbit(k);
}
return s;
}
void change(int p,int k,int v)
{
int v1=1<<(p+1);
while (k<=v1)
{
c[p][k]+=v;
k+=lowbit(k);
}
}
void turn(int wz,int v)
{
int j;
fo(j,0,20)
{
int v1=1<<j;
change(j,a[wz]%(v1*2)+1,v);
}
}
int find(int p,int l,int r)
{
l++;
r++;
int v=1<<(p+1);
if (l>r) swap(l,r);
if (r<=0) return gets(p,r+v)-gets(p,l+v-1);
else if (l<=0) return gets(p,r)+gets(p,v)-gets(p,l+v-1);
else return gets(p,r)-gets(p,l-1);
}
int main()
{
cin>>n>>m;
int i,j,k;
fo(i,1,n)
{
scanf("%d",&a[i]);
turn(i,1);
}
fo(i,1,m)
{
int p,x,y;
scanf("%d%d%d",&p,&x,&y);
if (p==1)
{
turn(x,-1);
a[x]=y;
turn(x,1);
}
else
{
long long ans=0,s1;
fo(j,0,20)
{
long long v=1<<j;
if ((v&y)==0) continue;
s1=find(j,v-x%(v*2),v*2-x%(v*2)-1);
ans+=s1*v;
}
printf("%lld\n",ans);
}
}
}