学习笔记 莫队
讲解
某大佬的博客
里面讲的很详细
解决问题
主要解决序列上的问题
比如区间的不同元素个数等等乱七八糟的
主要方法
通过离线把区间分块排序来让暴力效率更高
哪怕是待修也可以通过增加一维解决
排序比较函数
一般的:
inline bool cmp(node a,node b) {
return bl[a.l]==bl[b.l] ? a.r < b.r : bl[a.l] < bl[b.l];
}
有一定优化的:
先按照左端点的块排序假如块是奇数的时候右端点递增,否则递减
inline bool cmp1(node x,node y){
return (bl[x.l]^bl[y.l]) ? bl[x.l]<bl[y.l] : (bl[x.l]&1) ? x.r<y.r : x.r>y.r;
}
待修的:
增加了一维
inline bool cmp1(node x,node y){
return (bl[x.l]^bl[y.l]) ? bl[x.l]<bl[y.l] : ((bl[x.r]^bl[y.r]) ? bl[x.r]<bl[y.r] : x.ti < y.ti);
}
例题
SP3267 DQUERY - D-query
很正常的莫队,直接就是板子一样的
代码如下:
#include<bits/stdc++.h>
using namespace std;
const int maxn=30010,maxm=1e6+10;
int m,n,a[maxm],bl[maxm],ans[maxm],cnt[maxm];
struct node{
int l,r,id;
}q[maxm];
inline bool cmp1(node x,node y){
return (bl[x.l]^bl[y.l]) ? bl[x.l]<bl[y.l] : (bl[x.l]&1) ? x.r<y.r : x.r>y.r;
}
int main()
{
scanf("%d",&n);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
int sz=sqrt(n);
int bln=ceil((double)n/sz);
for(int i=1;i<=bln;i++)
for(int j=(i-1)*sz+1;j<=min(n,i*sz);j++) bl[j]=i;
scanf("%d",&m);
for(int i=1;i<=m;i++) scanf("%d%d",&q[i].l,&q[i].r),q[i].id=i;
sort(q+1,q+1+m,cmp1);
int l=1,r=0,now=0;
for(int i=1;i<=m;i++){
int ql=q[i].l,qr=q[i].r;
while(l<ql) now-=!--cnt[a[l++]];
while(l>ql) now+=!cnt[a[--l]]++;
while(r<qr) now+=!cnt[a[++r]]++;
while(r>qr) now-=!--cnt[a[r--]];
ans[q[i].id]=now;
}
for(int i=1;i<=m;i++) printf("%d
",ans[i]);
return 0;
}
Luogu_P2709 小B的询问
需要推一下加减的式子
代码如下:
#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=50010;
int n,m,k,a[maxn],bl[maxn],cnt[maxn];
ll now=0,ans[maxn];
struct node{
int l,r,id;
}e[maxn];
inline bool cmp1(node x,node y){
return (bl[x.l]^bl[y.l]) ? bl[x.l]<bl[y.l] : (bl[x.l]&1) ? x.r<y.r : x.r>y.r;
}
inline void add(int x){
cnt[a[x]]++;
now+=(ll)2*cnt[a[x]]-1;
}
inline void del(int x){
cnt[a[x]]--;
now-=(ll)2*cnt[a[x]]+1;
}
int main()
{
scanf("%d%d%d",&n,&m,&k);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
for(int i=1;i<=m;i++) scanf("%d%d",&e[i].l,&e[i].r),e[i].id=i;
int sz=sqrt(n);
int bln=ceil((double)n/sz);
for(int i=1;i<=bln;i++)
for(int j=sz*(i-1)+1;j<=min(n,sz*i);j++) bl[j]=i;
sort(e+1,e+1+m,cmp1);
int l=1,r=0;
for(int i=1;i<=m;i++){
int ql=e[i].l,qr=e[i].r;
while(l<ql) del(l++);
while(l>ql) add(--l);
while(r<qr) add(++r);
while(r>qr) del(r--);
ans[e[i].id]=now;
}
for(int i=1;i<=m;i++) printf("%lld
",ans[i]);
return 0;
}
P1903 [国家集训队]数颜色 / 维护队列
带修的,增加一维。
要是不把add和del加入主函数会T,挺卡常然而我O2
#include<bits/stdc++.h>
using namespace std;
const int maxn=200003,maxcnt=1e6+10;
int n,m,a[maxn],bl[maxn],qcnt,ccnt,now=0,ans[maxn],cnt[maxcnt];
struct node{
int l,r,ti,id;
}q[maxn];
struct edge{
int wh,cl;
}c[maxn];
inline bool cmp1(node x,node y){
return (bl[x.l]^bl[y.l]) ? bl[x.l]<bl[y.l] : ((bl[x.r]^bl[y.r]) ? bl[x.r]<bl[y.r] : x.ti < y.ti);
}
inline void add(int x){
if(!cnt[a[x]]) now++;
cnt[a[x]]++;
}
inline void del(int x){
cnt[a[x]]--;
if(!cnt[a[x]]) now--;
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++) scanf("%d",&a[i]);
int sz=pow(n,(double)2.0/3.0);
int bln=ceil((double)n/sz);
for(int i=1;i<=bln;i++)
for(int j=sz*(i-1)+1;j<=min(n,sz*i);j++) bl[j]=i;
for(int i=1;i<=m;i++){
char s[10];scanf("%s",s);
if(s[0]=='Q'){
++qcnt;scanf("%d%d",&q[qcnt].l,&q[qcnt].r);
q[qcnt].ti=ccnt;q[qcnt].id=qcnt;
}else{
++ccnt;scanf("%d%d",&c[ccnt].wh,&c[ccnt].cl);
}
}
sort(q+1,q+1+qcnt,cmp1);
int l=1,r=0,tme=0;now=0;
for(int i=1;i<=qcnt;i++){
int ql=q[i].l,qr=q[i].r,qt=q[i].ti;
while(l<ql) del(l++);
while(l>ql) add(--l);
while(r<qr) add(++r);
while(r>qr) del(r--);
while(tme<qt){
++tme;
if(ql<=c[tme].wh && c[tme].wh<=qr) {
cnt[a[c[tme].wh]]--;
if(!cnt[a[c[tme].wh]]) now--;
if(!cnt[c[tme].cl]) now++;
cnt[c[tme].cl]++;
}
swap(a[c[tme].wh],c[tme].cl);
}
while(tme>qt){
if(ql<=c[tme].wh && c[tme].wh<=qr) {
cnt[a[c[tme].wh]]--;
if(!cnt[a[c[tme].wh]]) now--;
if(!cnt[c[tme].cl]) now++;
cnt[c[tme].cl]++;
}
swap(a[c[tme].wh],c[tme].cl);
--tme;
}
ans[q[i].id]=now;
}
for(int i=1;i<=qcnt;i++) printf("%d
",ans[i]);
return 0;
}
易错点(我的)
cnt数组开不够空间
now的加减