For each case first line is n, m(1<=n ,m<=100000) indicate there are n men and m query.
Then a line have n number indicate the ID of men from left to right.
Next m line each line has two number L,R(1<=L<=R<=n),mean we want to know the answer of [L,R].
OutputFor every query output a number indicate there should be how many group so that the sum of value is max.Sample Input
1 5 2 3 1 2 5 4 1 5 2 4Sample Output
1 2
题意:t组数据,n,m,n个数,m个查询,每个查询为一个 区间,找这个区间中连续的串;
先用 数状数组 从左到右依次维护一边, 因为维护的时候,前面的影响后面的 线段数,所以在判断区间的线段数时,可以消除区间前面数的影响,这时候就应该 把所查询的 区间按照左端点从小到大排一下序;依次消除 区间前面数的印象;
我们用树状数组 维护线段数,从左到右 一次加入 判断 是不是一个新串,其实树状数组还是判断的位置,来维护线段数,判断当前 a[i] 自己 是不是个新串,主要判断 a[i] - 1和 a[i] + 1, 是不是前面已经判断过,是不是树状数组已经维护过了;
如果a[i]-1 和 a[i] + 1都没有维护过的话,那么 add(i,1) ,当前这个是个新串;
如果 a[i] - 1和a[i] + 1 有一个被维护过了,那么当前自己就可以 直接连上,线段数不变 add(i,0);
如果 a[i] -1 和a[i] + 1 都被维护过了,那么我们要做的就是把两个 线段连成一个线段; add(i,-1);
再消除点时,那么这个点就把当前这个 这条线段 分成了两段,a[i]-1 为一段,a[i]+1 这一部分为一段
所以add(pos[a[i]-1],1); add(pos[a[i] + 1],1), 为什么明明 一条线段变成两条线段,为啥增加了2;
因为当我们数状数组 维护的时候,一条线段 记数一定是记到 这条线段的最左边的那个位置上为1;所以去掉个 1 ,加上一2;
若 一个区间[l,r],连一条线段最左边的位置,都不包括的话,那么这条线段一定就不包括了;
自己可以模拟一边
代码:
#include<stdio.h>
#include<string.h>
#include<algorithm>
using namespace std;
#define Max 100010
int c[Max]; // 数状数组 ;
int a[Max]; // 存输入的值;
int pos[Max]; // pos[i] i的位置;
int vis[Max]; // vis[i] i 这个值是不是已经标记过;为判断 a[i]-1 和 a[i]+1,所在线段是不是已经记过数;
int ans[Max]; // 因为结构体排序,还得对应输出;
int n,m;
struct node
{
int x,y;
int p;
}stu[Max];
int cmp(node a,node b)
{
return a.x < b.x;
}
int bit(int x)
{
return x&-x;
}
void add(int x,int val)
{
while(x<=n)
{ c[x] += val;
x = x + bit(x);
}
}
int sum(int i)
{
int res = 0;
while(i>0)
{
res += c[i];
i -= bit(i);
}
return res;
}
int getsum(int l,int r)
{
return sum(r) - sum(l-1);
}
int main()
{
int i,j;
int t;
scanf("%d",&t);
while(t--)
{
memset(vis,0,sizeof(vis));
memset(c,0,sizeof(c));
scanf("%d%d",&n,&m);
for(i = 1;i<=n;i++)
{
scanf("%d",&a[i]);
pos[a[i]] = i;
}
for(i = 0;i<m;i++)
{
scanf("%d%d",&stu[i].x,&stu[i].y);
stu[i].p = i;
}
sort(stu,stu+m,cmp);
for(i = 1;i<=n;i++)
{
vis[a[i]] = 1;
if(!vis[a[i]-1]&&!vis[a[i]+1]) // 至今左右都不连续,单独是一个团;
add(i,1);
else if(vis[a[i]-1]&&vis[a[i]+1]) // 把两条直线合并成 1 条;
add(i,-1);
else add(i,0); // 这条线段 前面一定被记过数了;
}
i = 1;
int num = 0;
while(num<m)
{
int l = stu[num].x,r = stu[num].y;
for(;i<l;i++)
{
if(a[i]>1) add(pos[a[i]-1],1); // 当前 a[i] 删除掉,a[i]-1和 a[i]+1 所在位置上都加1;
if(a[i]<n) add(pos[a[i]+1],1);
}
ans[stu[num].p] = getsum(l,r);
//printf("p==%d\n",stu[].p);
num++;
}
for(i = 0;i<m;i++)
{
printf("%d\n",ans[i]);
}
}
return 0;
}