题目大意
给定一个序列,每个元素都有一个权值。
如果一些人是一组,当且仅当他们的下标连续。
每次询问[L,R]中组的最少个数。
题解
这题很容易想到莫队。
然后在考虑如何维护组的个数。
可以分开考虑:
1.插入时:
如果只有一边有元素,那么总组数不变。
如果两边都有,那么总组数减一,因为这个数的插入合并了两个组。
如果两边都没有,那么总组数加一,因为则个数自成一组。
2.删除时:
如果只有一边有元素,不变。
如果连边都有,那么总组数减一,因为产生了一组。
如果两边都没有,总组数减一,因为这组消失了。
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
const int M=100005;
int s;
struct Query{
int L,R,id;
void input(int x){
scanf("%d%d",&L,&R);
id=x;
}
bool operator <(const Query&tmp)const{
if(L/s==tmp.L/s) return R<tmp.R;
return L/s<tmp.L/s;
}
}q[M];
int A[M],res,ans[M];
bool mark[M];
void del(int x){
if(mark[A[x]-1]&&mark[A[x]+1]) res++;
if(!mark[A[x]-1]&&!mark[A[x]+1]) res--;
mark[A[x]]=0;
}
void add(int x){
if(!mark[A[x]-1]&&!mark[A[x]+1]) res++;
if(mark[A[x]-1]&&mark[A[x]+1]) res--;
mark[A[x]]=1;
}
void solve(){
int n,m;
scanf("%d%d",&n,&m);
s=(int)sqrt(n);
for(int i=0;i<=n+1;i++)
mark[i]=0;
for(int i=1;i<=n;i++)
scanf("%d",&A[i]);
for(int i=0;i<m;i++)
q[i].input(i);
sort(q,q+m);
int L=1,R=1;
res=1;
mark[A[1]]=1;
for(int i=0;i<m;i++){
for(;R<q[i].R;R++) add(R+1);
for(;R>q[i].R;R--) del(R);
for(;L<q[i].L;L++) del(L);
for(;L>q[i].L;L--) add(L-1);
ans[q[i].id]=res;
}
for(int i=0;i<m;i++)
printf("%d\n",ans[i]);
}
int main(){
int T;
scanf("%d",&T);
while(T--) solve();
return 0;
}