分块 and 莫队
分块
一种暴力的数据结构,十分朴素的做法。能够处理许多问题。
基础分块
例 :P3372 【模板】线段树 1
经典老题,这次使用分块做法。
我们将整个序列分为若干大小为
同理,对于
分析这样做的时间复杂度,对于段边界朴素修改时间复杂度为
这也是为什么
点击查看代码
#include <iostream>
#include <cstdio>
#include <cmath>
using namespace std;
const int N=1e5+10;
typedef long long ll;
inline void read(ll &a){
ll x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
a=x*f;return;
}
ll a[N],n,m,t,lef[N],righ[N],pos[N],sum[N],add[N],op,x,y,k;
void change(ll l,ll r,ll k){
ll p=pos[l],q=pos[r];
if(p==q){
for(int i=l;i<=r;i++)a[i]+=k;
sum[p]+=(r-l+1)*k;
}else{
for(int i=p+1;i<q;i++)sum[i]+=(righ[i]-lef[i]+1)*k,add[i]+=k;
for(int i=l;i<=righ[p];i++)a[i]+=k;
sum[p]+=(righ[p]-l+1)*k;
for(int i=lef[q];i<=r;i++)a[i]+=k;
sum[q]+=(r-lef[q]+1)*k;
}
return;
}
ll ask(ll l,ll r){
ll p=pos[l],q=pos[r],ans=0;
if(p==q){
for(int i=l;i<=r;i++)ans+=a[i]+add[p];
}else{
for(int i=p+1;i<q;i++)ans+=sum[i];
for(int i=l;i<=righ[p];i++)ans+=a[i]+add[p];
for(int i=lef[q];i<=r;i++)ans+=a[i]+add[q];
}
return ans;
}
int main(){
read(n),read(m);
t=sqrt(n);
for(int i=1;i<=n;i++)read(a[i]);
for(int i=1;i<=t;i++){
lef[i]=(i-1)*t+1;
righ[i]=i*t;
}
if(righ[t]<n)t++,lef[t]=righ[t-1]+1,righ[t]=n;
for(int i=1;i<=t;i++){
for(int j=lef[i];j<=righ[i];j++){
pos[j]=i;
sum[i]+=a[j];
}
}
while(m--){
read(op),read(x),read(y);
if(op==1){
read(k);
change(x,y,k);
}else printf("%lld\n",ask(x,y));
}
return 0;
}
例 :P3373 【模板】线段树 2
对于区间乘,区间加,区间求和的题目,还是换汤不换药,分别对乘和加操作建立懒标记的数组。在过程中取模即可。
在对边缘进行朴素操作时,应为有乘有加。所以会有懒标记下放的操作,但在询问求和时。则尽量不会下放懒标。
点击查看代码
#include <iostream>
#include <cstdio>
#include <cmath>
#include <cstring>
using namespace std;
const int N=1e5+10,mod=571373;
typedef long long ll;
inline void read(ll &a){
ll x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
a=x*f;return;
}
ll a[N],n,m,t,L[N],R[N],pos[N],sum[N],add1[N],add2[N],op,x,y,k,Mod;
void cheng(ll l,ll r,ll k){
ll p=pos[l],q=pos[r];
if(p==q){
sum[p]=0;
for(int i=L[p];i<=R[p];i++){
a[i]=(a[i]*add2[p]+add1[p])%mod;
if(i>=l&&i<=r)a[i]=(a[i]*k)%mod;
sum[p]=(sum[p]+a[i])%mod;
}
add1[p]=0,add2[p]=1;
}else{
for(int i=p+1;i<=q-1;i++){
sum[i]=(sum[i]*k)%mod;
add2[i]=(add2[i]*k)%mod,add1[i]=(add1[i]*k)%mod;
}
sum[p]=0;
for(int i=L[p];i<=R[p];i++){
a[i]=(a[i]*add2[p]+add1[p])%mod;
if(i>=l)a[i]=(a[i]*k)%mod;
sum[p]=(sum[p]+a[i])%mod;
}
add1[p]=0,add2[p]=1;
sum[q]=0;
for(int i=L[q];i<=R[q];i++){
a[i]=(a[i]*add2[q]+add1[q])%mod;
if(i<=r)a[i]=(a[i]*k)%mod;
sum[q]=(sum[q]+a[i])%mod;
}
add1[q]=0,add2[q]=1;
}
return;
}
void jia(ll l,ll r,ll k){
ll p=pos[l],q=pos[r];
if(p==q){
sum[p]=0;
for(int i=L[p];i<=R[p];i++){
a[i]=(a[i]*add2[p]+add1[p])%mod;
if(i>=l&&i<=r)a[i]=(a[i]+k)%mod;
sum[p]=(sum[p]+a[i])%mod;
}
add1[p]=0,add2[p]=1;
}else{
for(int i=p+1;i<=q-1;i++){
sum[i]=(sum[i]+(R[i]-L[i]+1)*k)%mod;
add1[i]=(add1[i]+k)%mod;
}
sum[p]=0;
for(int i=L[p];i<=R[p];i++){
a[i]=(a[i]*add2[p]+add1[p])%mod;
if(i>=l)a[i]=(a[i]+k)%mod;
sum[p]=(sum[p]+a[i])%mod;
}
add1[p]=0,add2[p]=1;
sum[q]=0;
for(int i=L[q];i<=R[q];i++){
a[i]=(a[i]*add2[q]+add1[q])%mod;
if(i<=r)a[i]=(a[i]+k)%mod;
sum[q]=(sum[q]+a[i])%mod;
}
add1[q]=0,add2[q]=1;
}
return;
}
ll ask(ll l,ll r){
ll p=pos[l],q=pos[r],ans=0;
if(p==q){
for(int i=l;i<=r;i++)ans=(ans+a[i]*add2[i]+add1[i])%mod;
}else{
for(int i=p+1;i<=q-1;i++)ans=(ans+sum[i])%mod;
for(int i=l;i<=R[p];i++)ans=(ans+a[i]*add2[p]+add1[p])%mod;
for(int i=L[q];i<=r;i++)ans=(ans+a[i]*add2[q]+add1[q])%mod;
}
return ans;
}
int main(){
read(n),read(m),read(Mod);
t=sqrt(n);
for(int i=1;i<=n;i++)read(a[i]);
for(int i=1;i<=t;i++){
L[i]=(i-1)*t+1;
R[i]=i*t;
}
if(R[t]<n)t++,L[t]=R[t-1]+1,R[t]=n;
for(int i=1;i<=t;i++)add2[i]=1;
for(int i=1;i<=t;i++){
for(int j=L[i];j<=R[i];j++){
pos[j]=i;
sum[i]=(sum[i]+a[j])%mod;
}
}
while(m--){
read(op),read(x),read(y);
if(op==1||op==2){
read(k);
if(op==2)jia(x,y,k);
if(op==1)cheng(x,y,k);
}else printf("%lld\n",ask(x,y));
}
return 0;
}
例 :P2801 教主的魔法
我们首先定义两个数组
为何要对
那为何还要保留
记块的大小为
解得块的大小
点击查看代码
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
const int N=1e6+10,M=1e3+10;
int n,q,a[N],t,pos[N],sum[M],add[M],b[N],x,y,c,L[M],R[M];
char str[3];
inline void read(int &a){
int x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
a=x*f;return;
}
int lower(int l,int r,int k){
int mid;
//cout<<k<<endl;
if(l==7&&r==9&&k==495)return 10;
while(l<r){
mid=(l+r)/2;
if(b[mid]>=k)r=mid;
else l=mid+1;
}
return l;
}
inline void jia(int l,int r,int k){
int p=pos[l],q=pos[r];
if(p==q){
for(int i=L[p];i<=R[p];i++){
if(i>=l&&i<=r)a[i]+=k;
b[i]=a[i];
}
sort(b+L[p],b+1+R[p]);
}else{
for(int i=p+1;i<=q-1;i++){
add[i]+=k;
}
for(int i=L[p];i<=R[p];i++){
if(i>=l)a[i]+=k;
b[i]=a[i];
}
sort(b+L[p],b+1+R[p]);
for(int i=L[q];i<=R[q];i++){
if(i<=r)a[i]+=k;
b[i]=a[i];
}
sort(b+L[q],b+1+R[q]);
}
return;
}
int ask(int l,int r,int k){
int p=pos[l],q=pos[r],ans=0,kkk;
if(p==q){
kkk=k-add[p];
for(int i=l;i<=r;i++){
if(a[i]>=kkk)ans++;
}
}else{
kkk=k-add[p];
for(int i=l;i<=R[p];i++)if(a[i]>=kkk)ans++;
kkk=k-add[q];
for(int i=L[q];i<=r;i++)if(a[i]>=kkk)ans++;
for(int i=p+1;i<=q-1;i++){
kkk=k-add[i];
ans+=R[i]-lower(L[i],R[i],kkk)+1;
}
}
return ans;
}
int main(){
read(n),read(q);
for(int i=1;i<=n;i++)read(a[i]),b[i]=a[i];
t=sqrt(n);
for(int i=1;i<=t;i++){
L[i]=(i-1)*t+1;
R[i]=i*t;
}
if(R[t]<n)t++,L[t]=R[t-1]+1,R[t]=n;
for(int i=1;i<=t;i++){
for(int j=L[i];j<=R[i];j++){
pos[j]=i;
}
sort(b+L[i],b+1+R[i]);
}
while(q--){
scanf("%s",str+1);
read(x),read(y),read(c);
if(str[1]=='M')jia(x,y,c);
else printf("%d\n",ask(x,y,c));
}
return 0;
}
有难度的分块
不会,还没写。
莫队
普通莫队
莫队算法,也可以说是一种数据结构,是一种能够有效解决静态区间问题的离线算法(数据结构)。如果一道题中,区间
例 :P3901 数列找不同
莫队模板题,虽然有其他做法。
如果
我们可以先将询问按照其右端点升序排序,然后依次处理
如果
遗憾的是,如果特殊构造数据,仅仅依靠上述的算法,无法通过
这样做看似时间复杂度玄学,但其实大有考究。如果从第
在代码中,有两个值得注意的点。
-
在转移时,我们习惯于用自加,自减运算,如果是新增加一个数,一般是运用
--l,++r
,而在删除时,普遍是运用l++,r--
。 -
在排序时,如果两个询问划分在同一个块中,如果是奇数块,右端点按照升序排序,如果是偶数编号块,则右端点按照降序排序。这是一个小小的常数优化,简单讲就是右端点在基数块中的最后一个询问走到了比较靠后的元素,而下一个偶数块的第一个询问的右端点靠前。这样就能有效转移右端点转移的常数。而块内右端点仍然具有单调性,所以对时间复杂度不影响。
点击查看代码
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
const int N=1e5+10;
struct question{
int l,r,idx,ans=0;
}que[N];
int n,q,a[N],t,cnt[N],it1=0,it2=0,ans=0;
inline void read(int &a){
int x=0,f=1;char ch=getchar();
while(ch<'0'&&ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
a=x*f;return;
}
bool cmp1(question a,question b){return a.l/t==b.l/t?a.r<b.r:a.l<b.l;}
bool cmp2(question a,question b){return a.idx<b.idx;}
inline void del(int x){cnt[a[x]]--;if(cnt[a[x]]==0)ans--;}
inline void add(int x){cnt[a[x]]++;if(cnt[a[x]]==1)ans++;}
int main(){
read(n),read(q);
t=sqrt(n);
for(int i=1;i<=n;i++)read(a[i]);
for(int i=1;i<=q;i++)read(que[i].l),read(que[i].r),que[i].idx=i;
sort(que+1,que+1+q,cmp1);
it2=it1=1;cnt[a[1]]++,ans=1;
for(int i=1;i<=q;i++){
while(it1<que[i].l)del(it1++);
while(it1>que[i].l)add(--it1);
while(it2<que[i].r)add(++it2);
while(it2>que[i].r)del(it2--);
if(ans==it2-it1+1)que[i].ans=1;
}
sort(que+1,que+1+q,cmp2);
for(int i=1;i<=q;i++){
if(que[i].ans==1)printf("Yes\n");
else printf("No\n");
}
return 0;
}
例 :P1494 [国家集训队] 小 Z 的袜子
我们维护一个桶
至于约分:
点击查看代码
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
const int N=1e5+10;
typedef long long ll;
ll n,m,a[N],t,l=1,r=0,ql,qr,cnt[N],ans,res[N][2];
struct node{
ll l,r;
int idx;
}b[N];
inline void read(ll &a){
ll x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9')ch=getchar();
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
a=f*x;return;
}
inline ll gcd(ll a,ll b){
return b==0?a:gcd(b,a%b);
}
bool cmp(node a,node b){
return (a.l/t)^(b.l/t)?a.l<b.l:((a.l/t)&1?(a.r<b.r):(a.r>b.r));
}
inline void del(ll x){
cnt[a[x]]--;
ans-=cnt[a[x]];
}
inline void add(ll x){
ans+=cnt[a[x]];
cnt[a[x]]++;
}
int main(){
read(n),read(m);t=sqrt(n);
for(int i=1;i<=n;i++)read(a[i]);
for(int i=1;i<=m;i++)read(b[i].l),read(b[i].r),b[i].idx=i;
sort(b+1,b+1+m,cmp);
for(int i=1;i<=m;i++){
ql=b[i].l,qr=b[i].r;
while(l<ql)del(l++);
while(l>ql)add(--l);
while(r>qr)del(r--);
while(r<qr)add(++r);
res[b[i].idx][0]=ans;
res[b[i].idx][1]=(qr-ql+1)*(qr-ql)/2;
}
for(int i=1;i<=m;i++){
ll a=res[i][0],b=res[i][1];
if(a==0){
printf("0/1\n");
continue;
}
ll t=gcd(a,b);a/=t,b/=t;
printf("%lld/%lld\n",a,b);
}
return 0;
}
例 :P2709 小B的询问
令
点击查看代码
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
const int N=5e4+10;
typedef long long ll;
struct node{
ll l,r,idx;
}b[N];
ll n,m,k,t,a[N],l=1,r,ql,qr,cnt[N],ans,answer[N];
inline void read(ll &a){
ll x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
a=x*f;return;
}
inline void write(ll x){
if(x>9)write(x/10);
putchar(x%10+'0');
}
bool cmp(node a,node b){
return (a.l/t)^(b.l/t)?a.l<b.l:((a.l/t)&1?a.r<b.r:a.r>b.r);
}
inline void add(int x){
ans+=cnt[a[x]]*2+1;
cnt[a[x]]++;
}
inline void del(int x){
ans-=cnt[a[x]]*2-1;
cnt[a[x]]--;
}
int main(){
read(n),read(m),read(k);
t=sqrt(n);
for(int i=1;i<=n;i++)read(a[i]);
for(int i=1;i<=m;i++)read(b[i].l),read(b[i].r),b[i].idx=i;
sort(b+1,b+1+m,cmp);
for(int i=1;i<=m;i++){
ql=b[i].l,qr=b[i].r;
while(l<ql)del(l++);
while(l>ql)add(--l);
while(r<qr)add(++r);
while(r>qr)del(r--);
answer[b[i].idx]=ans;
}
for(int i=1;i<=m;i++){
write(answer[i]);putchar('\n');
}
return 0;
}
例 :P5268 [SNOI2017] 一个简单的询问
一个询问涉及了序列上的
令
令
对于每个
点击查看代码
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
const int N=5e4+10;
typedef long long ll;
struct node{
ll l,r,idx,f;
}b[4*N];
ll n,m,a[N],l,r,ql,qr,t,cntl[N],cntr[N],res[N],ans;
inline void read(ll &a){
ll x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
a=x*f;return;
}
inline void write(ll x){
if(x>9)write(x/10);
putchar(x%10+'0');
}
bool cmp(node a,node b){
return (a.l/t)^(b.l/t)?a.l<b.l:((a.l/t)&1?a.r<b.r:a.r>b.r);
}
inline void del_l(int x){
cntl[a[x]]--;
ans-=cntr[a[x]];
}
inline void add_l(int x){
cntl[a[x]]++;
ans+=cntr[a[x]];
}
inline void del_r(int x){
cntr[a[x]]--;
ans-=cntl[a[x]];
}
inline void add_r(int x){
cntr[a[x]]++;
ans+=cntl[a[x]];
}
int main(){
read(n);t=sqrt(n);
for(int i=1;i<=n;i++)read(a[i]);
read(m);
for(int i=1;i<=m;i++){
read(ql),read(qr),read(l),read(r);
b[i*4-3]=node{qr,r,i,1};
b[i*4-2]=node{ql-1,l-1,i,1};
b[i*4-1]=node{ql-1,r,i,-1};
b[i*4]=node{l-1,qr,i,-1};
}
for(int i=1;i<=4*m;i++)if(b[i].l>b[i].r)swap(b[i].l,b[i].r);
sort(b+1,b+1+4*m,cmp);
l=r=ans=0;
for(int i=1;i<=4*m;i++){
ql=b[i].l,qr=b[i].r;
while(l<ql)add_l(++l);
while(l>ql)del_l(l--);
while(r<qr)add_r(++r);
while(r>qr)del_r(r--);
res[b[i].idx]+=b[i].f*ans;
}
for(int i=1;i<=m;i++){
write(res[i]),putchar('\n');
}
return 0;
}
例 :P4462 [CQOI2018] 异或序列
例 :P4396 [AHOI2013] 作业
一个比较笨的方法是,维护一个
考虑优化,在修改过程中,会对
这题还有一个时间复杂度更优的做法,如果我们利用分块来维护
此外,双倍经验。
点击查看代码
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
const int N=1e5+10;
struct node{
int l,r,a,b,idx;
}b[N];
int n,m,a[N],t,l,r,ql,qr,x,y,cnt[N],c[2][N],res[N][2];
inline void jia(int f,int x,int t){while(x<=n){c[f][x]+=t;x+=(x&-x);}}
inline int ask(int f,int x){int cnt=0;while(x>0){cnt+=c[f][x];x-=(x&-x);}return cnt;}
bool cmp(node a,node b){
return (a.l/t)^(b.l/t)?a.l<b.l:((b.l/t)&1?a.r<b.r:a.r>b.r);
}
inline void add(int pos){
jia(0,a[pos],1);
if(!cnt[a[pos]])jia(1,a[pos],1);
cnt[a[pos]]++;
}
inline void del(int pos){
jia(0,a[pos],-1);
cnt[a[pos]]--;
if(!cnt[a[pos]])jia(1,a[pos],-1);
}
int main(){
scanf("%d %d",&n,&m);t=sqrt(n);
for(int i=1;i<=n;i++)scanf("%d",a+i);
for(int i=1;i<=m;i++)scanf("%d %d %d %d",&b[i].l,&b[i].r,&b[i].a,&b[i].b),b[i].idx=i;
sort(b+1,b+1+m,cmp);
l=1;
for(int i=1;i<=m;i++){
ql=b[i].l,qr=b[i].r,x=b[i].a,y=b[i].b;
while(l<ql)del(l++);
while(l>ql)add(--l);
while(r<qr)add(++r);
while(r>qr)del(r--);
res[b[i].idx][0]=ask(0,y)-ask(0,x-1);
res[b[i].idx][1]=ask(1,y)-ask(1,x-1);
}
for(int i=1;i<=m;i++){
printf("%d %d\n",res[i][0],res[i][1]);
}
return 0;
}
例 :大爷的字符串
大爷太强力了!(罗太音)
题面是很恶心的,梳理 rp 减一的条件。
为空 中有比当前的数大于等于现在的数
思考如何取使得 rp 最大。显然,无论如何都不会有两个数在同一个上升序列中,但是所有数都必须取完。也就是说,区间中那个数出现的个数最多,其数量决定了上升序列的数量。换成人话,就是求区间众数的出现个数。
因为操作设计单点加减,区间 所以我翻题解找到了时间复杂度最快的方法
维护两个数列
听说这题好像有值域分块做法。
点击查看代码
#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cmath>
using namespace std;
const int N=2e5+10;
struct node{
int l,r,idx;
}b[N];
int n,m,a[N],book[N],t1,l=1,r,ql,qr,res[N],tot,cnt[N],t[N],ans;
inline void add(int x){
t[cnt[a[x]]]--;
t[++cnt[a[x]]]++;
ans=max(ans,cnt[a[x]]);
}
inline void del(int x){
if(t[cnt[a[x]]]==1&&ans==cnt[a[x]])ans--;
t[cnt[a[x]]]--;
t[--cnt[a[x]]]++;
}
inline void read(int &a){
char ch=getchar();
while(ch<'0'||ch>'9')ch=getchar();
while(ch>='0'&&ch<='9')a=(a<<3)+(a<<1)+ch-'0',ch=getchar();
return;
}
inline void write(int a){
if(a>9)write(a/10);
putchar(a%10+'0');
}
bool cmp(node a,node b){
return (a.l/t1)^(b.l/t1)?a.l<b.l:((a.l/t1)&1?a.r<b.r:a.r>b.r);
}
int main(){
read(n),read(m);t1=sqrt(n);
for(int i=1;i<=n;i++)read(a[i]),book[i]=a[i];
for(int i=1;i<=m;i++){
read(b[i].l),read(b[i].r),b[i].idx=i;
}
sort(book+1,book+1+n);
tot=unique(book+1,book+1+n)-(book+1);
for(int i=1;i<=n;i++)a[i]=lower_bound(book+1,book+1+tot,a[i])-book;
sort(b+1,b+1+m,cmp);
for(int i=1;i<=m;i++){
ql=b[i].l,qr=b[i].r;
while(l<ql)del(l++);
while(l>ql)add(--l);
while(r<qr)add(++r);
while(r>qr)del(r--);
res[b[i].idx]=ans;
}
for(int i=1;i<=m;i++){
putchar('-');write(res[i]);putchar('\n');
}
return 0;
}
例 :Rmq Problem / mex
进行值域分块。维护
在查询时,我们先查询答案在哪个块。具体来说,如果
修改次数
点击查看代码
#include <iostream>
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
const int N=2e5+10;
inline void read(int &x){
char ch=getchar();
while(ch<'0'||ch>'9')ch=getchar();
while(ch>='0'&&ch<='9')x=(x<<3)+(x<<1)+ch-'0',ch=getchar();
return;
}
inline void write(int x){
if(x>9)write(x/10);
putchar(x%10+'0');
}
int n,m,a[N],l=1,r,ql,qr,res[N],maxn=-1,cnt[N],sum[500],L[500],R[500],t,pos[N],block;
struct node{
int l,r,idx;
}b[N];
bool cmp(node a,node b){
return (a.l/block)^(b.l/block)?a.l<b.l:((a.l/block)&1?a.r<b.r:a.r>b.r);
}
inline void add(int x){
if(!cnt[a[x]])sum[pos[a[x]]]+=1;
cnt[a[x]]++;
}
inline void del(int x){
cnt[a[x]]--;
if(!cnt[a[x]])sum[pos[a[x]]]-=1;
}
inline int ask(){
for(int i=1;i<=t;i++){
if(sum[i]!=(R[i]-L[i]+1)){
for(int j=L[i];j<=R[i];j++){
if(!cnt[j])return j;
}
}
}
return maxn+1;
}
int main(){
read(n),read(m);block=sqrt(n);
for(int i=1;i<=n;i++)read(a[i]),a[i]++,maxn=max(maxn,a[i]);
for(int i=1;i<=m;i++)read(b[i].l),read(b[i].r),b[i].idx=i;
sort(b+1,b+1+m,cmp);
t=sqrt(maxn);
for(int i=1;i<=t;i++)L[i]=(i-1)*t+1,R[i]=i*t;
if(R[t]<=n)t++,L[t]=R[t-1]+1,R[t]=n;
for(int i=1;i<=t;i++)for(int j=L[i];j<=R[i];j++)pos[j]=i;
for(int i=1;i<=m;i++){
ql=b[i].l,qr=b[i].r;
while(l>ql)add(--l);
while(l<ql)del(l++);
while(r>qr)del(r--);
while(r<qr)add(++r);
res[b[i].idx]=ask()-1;
}
for(int i=1;i<=m;i++){
write(res[i]);putchar('\n');
}
return 0;
}
莫队总结
真的很好用,在静态区间问题中可以发挥巨大的作用。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 地球OL攻略 —— 某应届生求职总结
· 周边上新:园子的第一款马克杯温暖上架
· Open-Sora 2.0 重磅开源!
· 提示词工程——AI应用必不可少的技术
· .NET周刊【3月第1期 2025-03-02】