Description
Solution
比赛的时候想了半天的线段树,结果发现....
有结论:
当y的第i位为1,并且:
2i⩽(a+x)mod2i+1⩽2i+1−1
时,a的第i位二进制有效,
移项:
设 x′=xmod2i+1
2i−x′⩽(amod2i+1)⩽2i+1−1−x′
再判断一下中间项超出模时的情况,
所以对于所有数的每位二进制,只要统计在 [2i−x′,2i+1−1−x′] 和 [2i−x′,2i+1−1−x′] 这两个区间的合法个数即可判断此二进制位合法的个数,
修改就直接修改,
这个可以用树状数组来做,。
复杂度: O(nlog2(n))
Code
#include<cstdio>
#include<cstdlib>
#define fo(i,a,b) for(int i=a;i<=b;i++)
#define NX(q) ((q)&(-q))
using namespace std;
typedef long long LL;
const int N=100500,M=20,M1=1<<M;
int read(int &n)
{
char ch=' ';int q=0,w=1;
for(;(ch!='-')&&((ch<'0')||(ch>'9'));ch=getchar());
if(ch=='-')w=-1,ch=getchar();
for(;ch>='0' && ch<='9';ch=getchar())q=q*10+ch-48;n=q*w;return n;
}
int m,n;
LL ans;
int a[N];
int er[M+3];
int f[M+3][M1+1];
int min(int q,int w){return q<w?q:w;}
void change(int E,int q,int e){q++;while(q<=M1)f[E][q]+=e,q+=NX(q);}
int find(int E,int q)
{
int ans=0;q++;q=min(q,M1);
while(q>0)ans+=f[E][q],q-=NX(q);
return ans;
}
int main()
{
er[1]=1;fo(i,2,M+2)er[i]=er[i-1]<<1;
int q,w,e;
read(n),read(m);
fo(i,1,n)
{
read(a[i]);
fo(j,1,M)change(j,a[i]%er[j+1],1);
}
fo(i,1,m)
{
read(q),read(w),read(e);
if(q==1)
{
fo(j,1,M)if((a[w]%er[j+1])!=(e%er[j+1]))change(j,a[w]%er[j+1],-1),change(j,e%er[j+1],1);
a[w]=e;
}
else
{
ans=0;
fo(i,1,M)if(er[i]&e)ans+=(LL)er[i]*(find(i,er[i+1]-1-w%er[i+1])-find(i,er[i]-1-w%er[i+1])+find(i,er[i+1]+er[i+1]-1-w%er[i+1])-find(i,er[i+1]+er[i]-1-w%er[i+1]));
printf("%lld\n",ans);
}
}
return 0;
}