题目大意:求查询区间内异或和等于k的区间个数
难度:较简单
和这题类似有这道:CF86D Powerful array 与 洛谷P2709 小B的询问
1、l 到 r 的区间异或和 等于 1 到 r 的异或和 异或 1 到 l 的异或和,所以储存异或前缀和即可
2、遍历到pos位时,当前等于k的区间 等于 区间异或和等于 a[pos]^k 的个数,所以用一个mp储存一下异或出现的结果个数(最好用数组,map或set可能会超时)
3、增区间,先算贡献;减区间,先算影响,后算贡献;
const int maxn=1e5+7;
int n,m,size,k,a[maxn];
ll ans[maxn],mp[maxn*20],sum;
struct node{
int l,r,id;
bool operator<(const node &a)const{
if(l/size!=a.l/size) return l<a.l;
else if((l/size)&1) return r<a.r;
else return r>a.r;
}
}q[maxn];
int main(){
n=read(); m=read(); k=read(); size=sqrt(n);
for(int i=1;i<=n;i++){
a[i]=read();
a[i]^=a[i-1];
}
for(int i=0;i<m;i++){
q[i].l=read()-1; q[i].r=read();
q[i].id=i;
}
sort(q,q+m);
int l=1,r=0;
for(int i=0;i<m;i++){
int x=q[i].l,y=q[i].r;
while(l<x){
mp[a[l]]--;
sum-=mp[a[l]^k];
l++;
}
while(l>x){
l--;
sum+=mp[a[l]^k];
mp[a[l]]++;
}
while(r<y){
r++;
sum+=mp[a[r]^k];
mp[a[r]]++;
}
while(r>y){
mp[a[r]]--;
sum-=mp[a[r]^k];
r--;
}
ans[q[i].id]=sum;
}
for(int i=0;i<m;i++) out(ans[i]),putchar('\n');
}
-
P1494 [国家集训队]小Z的袜子 (进阶题)
这种题一般要推导出一个公式
可以看出莫队维护便是平方数,更改些地方,就出来了
const int maxn=1e6+7;
int n,m,a[maxn],size,mp[maxn];
ll ans1[maxn],ans2[maxn],sum;
struct node{
int l,r,id;
bool operator<(const node &a)const{
if(l/size!=a.l/size) return l<a.l;
else if((l/size)&1) return r<a.r;
else return r>a.r;
}
}q[maxn];
void add(int i){
sum-=mp[a[i]]*mp[a[i]];
mp[a[i]]++;
sum+=mp[a[i]]*mp[a[i]];
}
void sub(int i){
sum-=mp[a[i]]*mp[a[i]];
mp[a[i]]--;
sum+=mp[a[i]]*mp[a[i]];
}
int main(){
n=read(); m=read(); size=sqrt(n);
for(int i=1;i<=n;i++) a[i]=read();
for(int i=0;i<m;i++){
q[i].l=read(); q[i].r=read();
q[i].id=i;
}
sort(q,q+m);
int l=1,r=0;
for(int i=0;i<m;i++){
int x=q[i].l,y=q[i].r,k=q[i].id;
while(l<x) sub(l++);
while(l>x) add(--l);
while(r<y) add(++r);
while(r>y) sub(r--);
ans1[k]=sum-1ll*(r-l+1);
ans2[k]=1ll*(r-l+1)*(r-l);
if(!ans1[k]){
ans2[k]=1;
continue;
}
ll tmp=__gcd(ans1[k],ans2[k]);
ans1[k]/=tmp; ans2[k]/=tmp;
}
for(int i=0;i<m;i++) printf("%lld/%lld\n",ans1[i],ans2[i]);
}
-
HDU6610—Game (带修莫队+博弈)
题目大意:A、B玩尼姆博弈,A先选一个大区间[ L , R ] ,B再在这个大区间选一个小区间[ l , r ] ,最后在这个小区间内尼姆博弈,区间异或和不等于0时,先手A必胜。同时加了个操作,给出pos位,交换pos位和pos+1位的数。
精简一下: 给你n个数的数组,m次操作。操作1:将x与x+1位的数字交换 ;操作2:询问 [L,R]多少个子区间[l,r]里面的所有数值异或起来不等于0
带修莫队,块大小size=(int)pow(n,2.0/3.0)
mp数组记录异或和为x的次数,因为是前缀异或和,查询的区间的左区间自动减一。
int n,m,a[maxn],size,b[maxn],c1,c2,p[maxn];
ll mp[maxn*10],ans[maxn],sum;
struct node{
int l,r,id,time;
bool operator<(const node &a)const{
if(l/size!=a.l/size) return l<a.l;
else if(r/size!=a.r/size) return r<a.r;
else return time<a.time;
}
}q[maxn];
void add(int i){ sum+=mp[b[i]]++; }
void sub(int i){ sum-=--mp[b[i]]; }
void change(int i,int t){
int pos=p[t];
bool f=(q[i].l<=pos&&pos<=q[i].r);
if(f) sub(pos);
b[pos]^=a[pos]; swap(a[pos],a[pos+1]); b[pos]^=a[pos];
if(f) add(pos);
}
int main(){
while(~scanf("%d%d",&n,&m)){
memset(mp,0,sizeof(mp)); sum=c1=c2=0;
size=(int)pow(n,2.0/3.0);
for(int i=1;i<=n;i++){
a[i]=read();
b[i]=b[i-1]^a[i];
}
while(m--){
int op=read();
if(op==1){
q[c1].l=read()-1; q[c1].r=read();
q[c1].time=c2; q[c1].id=c1;
c1++;
}
else p[++c2]=read();
}
sort(q,q+c1);
int l=1,r=0,t=0;
for(int i=0;i<c1;i++){
int x=q[i].l,y=q[i].r,k=q[i].id,tmp=q[i].time;
while(l<x) sub(l++);
while(l>x) add(--l);
while(r<y) add(++r);
while(r>y) sub(r--);
while(t<tmp) change(i,++t);
while(t>tmp) change(i,t--);
ans[k]=(ll)(y-x+1)*(y-x)/2-sum;
}
for(int i=0;i<c1;i++) out(ans[i]),putchar('\n');
}
}
-
P4074 [WC2013] 糖果公园 树上带修莫队 (难题)
虽然融合了莫队的两大知识点(带修+树上),但这套题很适合做模板
涉及到:
1、带修排序方式,先 l ,后 r ,最后 time。
2、时间戳
3、欧拉序的规律
4、倍增求lca
const int maxn=2e5+7;
int n,m,q,size,c1,c2;
int v[maxn],w[maxn],a[maxn],mp[maxn],vis[maxn];
int tot,d[maxn],head[maxn],ft[maxn],lt[maxn],fa[maxn][30],ou[maxn],c;
ll sum,ans[maxn];
struct edge{ int to,next; }e[maxn];
struct point{ int id,val; }p[maxn];
struct node{
int l,r,id,time,lca;
bool operator<(const node &a)const{
if(l/size!=a.l/size) return l<a.l;
else if(r/size!=a.r/size) return r<a.r;
else return time<a.time;
}
}sz[maxn];
void Add(int u,int v){
e[++tot]=(edge){v,head[u]}; head[u]=tot;
e[++tot]=(edge){u,head[v]}; head[v]=tot;
}
void dfs(int x){
ou[++c]=x; ft[x]=c;
for(int i=head[x];i;i=e[i].next){
int t=e[i].to;
if(t==fa[x][0]) continue;
d[t]=d[x]+1; fa[t][0]=x;
for(int j=1;(1<<j)<=d[t];j++) fa[t][j]=fa[fa[t][j-1]][j-1];
dfs(t);
}
ou[++c]=x; lt[x]=c;
}
int Lca(int u,int v){
if(d[u]<d[v]) swap(u,v);
for(int i=20;i>=0;i--)
if(d[u]-(1<<i)>=d[v]) u=fa[u][i];
if(u==v) return u;
for(int i=20;i>=0;i--)
if(fa[u][i]!=fa[v][i]) u=fa[u][i],v=fa[v][i];
return fa[u][0];
}
void add(int i){ sum+=1ll*v[i]*w[++mp[i]]; }
void sub(int i){ sum-=1ll*v[i]*w[mp[i]--]; }
void cc(int i){
vis[i]^=1;
if(vis[i]) add(a[i]);
else sub(a[i]);
}
void change(int t){
int tmp=p[t].id;
if(vis[tmp]) sub(a[tmp]);
swap(a[tmp],p[t].val);
if(vis[tmp]) add(a[tmp]);
}
int main(){
n=read(); m=read(); q=read();
size=(int)pow(n,2.0/3.0);
for(int i=1;i<=m;i++) v[i]=read();
for(int i=1;i<=n;i++) w[i]=read();
for(int i=1;i<n;i++) Add(read(),read());
for(int i=1;i<=n;i++) a[i]=read();
d[1]=1; dfs(1);
while(q--){
int op=read(),x=read(),y=read();
if(op==1){
if(d[x]>d[y]) swap(x,y);
int lca=Lca(x,y);
if(lca==x) sz[c1]=(node){ft[x],ft[y],c1,c2,0};
else sz[c1]=(node){lt[x],ft[y],c1,c2,lca};
c1++;
}
else p[++c2]=(point){x,y};
}
sort(sz,sz+c1);
int l=1,r=0,t=0;
for(int i=0;i<c1;i++){
int x=sz[i].l,y=sz[i].r,tmp=sz[i].time,lca=sz[i].lca,k=sz[i].id;
while(l<x) cc(ou[l++]);
while(l>x) cc(ou[--l]);
while(r<y) cc(ou[++r]);
while(r>y) cc(ou[r--]);
while(t<tmp) change(++t);
while(t>tmp) change(t--);
if(lca) cc(lca);
ans[k]=sum;
if(lca) cc(lca);
}
for(int i=0;i<c1;i++) out(ans[i]),putchar('\n');
}