您的位置:首页 > 产品设计 > UI/UE

CC DEC.17 Chef And Easy Xor Queries 分块+懒标记

2017-12-12 16:57 316 查看
题意:长度为n的序列a,Q次操作,操作1:(i,x)将第a[i]变为x. 操作2:(i,k) 问有多少个前缀j(j<=r)其异或和为k.

n,Q<=1e5,a[i],x,k<=1e6.

暴力的话 每次单点修改a[i],都要修改pre[j](j>=i)的前缀和.每次查询都要查询每个pre[i](i<=r) O(n*Q).

现在对pre来分块.每块维护cnt[i][x]:该块pre值为x的有多少个.

修改一个a[i]->y.

对于每一块,每个x出现cnt次 则现在变为x^a[i]^y 出现cnt次

1<=x<=1e5,不能对块内元素一个一个更新次数 则对每块都加上一个标记val[i],表示该块内每个pre真实值为pre[j]^val[i].

非整块的点 直接修改它的pre值和cnt值即可.

对于查询 非整块直接查询 注意其真实值为pre[i]^val[pos[i]] O(N*sqrt(N)).

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=120000,M=350;
int n,Q,a
,op,p,r,k,x,pre
,val
,m,num,pos
;
int cnt[M][N*10];
void init()
{
m=(int)sqrt(n),num=n/m;
if(n%m)
num++;
for(int i=1;i<=n;i++)
pos[i]=(i-1)/m+1,val[i]=0;
for(int i=1;i<=num;i++)
for(int j=(i-1)*m+1;j<=n&&j<=i*m;j++)
cnt[i][pre[j]]++;
}
int main()
{
scanf("%d%d",&n,&Q);
for(int i=1;i<=n;i++)
scanf("%d",&a[i]),pre[i]=pre[i-1]^a[i];
init();
while(Q--)
{
scanf("%d",&op);
if(op==1)
{
scanf("%d%d",&p,&x);
for(int i=p;i<=pos[p]*m;i++)
{
cnt[pos[p]][pre[i]]--;
cnt[pos[p]][pre[i]^a[p]^x]++;
pre[i]=pre[i]^a[p]^x;
}
for(int i=pos[p]+1;i<=num;i++)
val[i]=val[i]^a[p]^x;
a[p]=x;
}
else
{
scanf("%d%d",&r,&k);
//for(int i=1;i<=n;i++)
// cout<<i<<' '<<(pre[i]^val[pos[i]])<<endl;
int res=0;
for(int i=1;i<=pos[r]-1;i++)
res+=cnt[i][k^val[i]];
for(int i=(pos[r]-1)*m+1;i<=r;i++)
if((pre[i]^val[pos[i]])==k)
res++;
printf("%d\n",res);
}
}
return 0;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: