Group
题目链接
题意:有n个数的排列,然后有m个询问,然后对于连续的一段数,这些数可以作为一个分组,然后对于每次询问给定一个区间[ L,R ] ,问这个区间内有多少个组。
分析: 可以用莫队算法,对于当前已知区间[ L,R ] 的答案,我们可以O(1)去得到(L+1,R),(L-1,R),(L,R+1),(L,R-1)的答案,可以用对于当前的点 u, 如果加上他,如果flag[u-1],flag[u+1]已经在区间中,说明区间(答案)可以减1,如果flag[u-1]和flag[u+1]都没有被标记,说明加上他区间(答案)得加1,存在一个标记不改变答案;对于减去当前点 u ,则相反。
注意:就是有可能就是两个区间是不相交的,这个得特殊判断
代码:
#include<iostream>
#include<string>
#include<cstdio>
#include<cstring>
#include<vector>
#include<math.h>
#include<map>
#include<queue>
#include<algorithm>
using namespace std;
const int inf = 0x3f3f3f3f;
typedef pair<int,int> pii;
const int maxn=1e5+10;
int R,L,ans;
int res[maxn];
struct node {
int l,r,id;
}p[maxn];
int pos[maxn];//表示在第几个块
bool cmp(node a,node b){//排序
if (pos[a.l]==pos[b.l])return pos[a.r]<pos[b.r];
return pos[a.l]<pos[b.l];
}
int a[maxn],n,m;
int flag[maxn];
void add(int x){//加入一个数
if (flag[a[x]])return;
if (flag[a[x]-1]&&flag[a[x]+1])ans--;//区间减一
else if (flag[a[x]-1]==0&&flag[a[x]+1]==0)ans++;//区间加一
flag[a[x]]=1;//加上标记
}
void del(int x){
if (flag[a[x]]==0)return;
if (flag[a[x]-1]&&flag[a[x]+1])ans++;//区间加一
else if (flag[a[x]-1]==0&&flag[a[x]+1]==0)ans--;//区间减一
flag[a[x]]=0;//减去标记
}
int main ()
{
int T;
scanf ("%d",&T);
while (T--){
scanf ("%d%d",&n,&m);
memset (flag,0,sizeof (flag));
int block=sqrt(n);
for (int i=1;i<=n;i++){
scanf ("%d",&a[i]);
pos[i]=(i-1)/block+1;//得到快的位置
}
for (int i=1;i<=m;i++){
scanf ("%d%d",&p[i].l,&p[i].r);
p[i].id=i;
}
sort(p+1,p+1+m,cmp);
L=1;R=0;ans=0;
for (int i=1;i<=m;i++){
if (p[i].l>R||p[i].r<L){//区间不相交
while (L<=R){
flag[a[L++]]=0;
}
ans=0;
L=p[i].l;
R=p[i].l-1;
while (R<p[i].r){
R++;
add(R);
}
res[p[i].id]=ans;
continue;
}
while (L<p[i].l){
del(L);
L++;
}
while (L>p[i].l){
L--;
add(L);
}
while (R<p[i].r){
R++;
add(R);
}
while (R>p[i].r){
del(R);
R--;
}
res[p[i].id]=ans;
}
for (int i=1;i<=m;i++){
printf ("%d\n",res[i]);
}
}
return 0;
}
/*
1
5 2
3 1 2 5 4
1 2
4 5
*/