主要借鉴这一篇:http://www.2cto.com/kf/201308/233187.html
思路:如果我们从左到右一个个加进来,那么对于加进来的第i个数num[i],那么它就能增加一个段(num[i]+1,和num[i]-1在之前都没出现过),或者减少一个段(num[i]+1,和num[i]-1在之前都出现过),或者不增不减(num[i]+1,和num[i]-1在之前只出现过其中一个)。那么我们要找的询问的区间[l,r]内有多少个区间,就是询问[l,r]内的数,增加了几个这样的段。但是直接询问[l,r]内的数产生的段数是不可以的,因为对于[l,r]内的某一个数,有可能它所能产生的段数是跟l之前,或者r之后的数有关的。因此我们可以将询问离线出来,按r排好序,每次询问之前,将r之前的数产生的影响先清除掉,也就是对于某个在r之前的数,将它所能影响的后面的数能增加的段去除,对于之前的已经不用管了,因为我们按右端点排序,所以在清除某一个数时,它前面的数的影响之前已经处理掉了。所以这个区间的段数自然就是[l,r]的区间和。
线段树和树状数组都能做,这里附上两份代码:
线段树:
#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <map>
#include <vector>
#include <cmath>
#include <stack>
#include <queue>
#include <cstdlib>
#include <algorithm>
using namespace std;
typedef __int64 int64;
typedef long long ll;
#define M 100005
#define mod 1000000007
struct node
{
int l , r , val;
}tree[M*4] , tp[M];
int num[M] , pos[M] , ans[M];
bool vis[M];
bool cmp(node a , node b)
{
if (a.r < b.r)return true;
return false;
}
void Build(int l , int r , int rt)
{
tree[rt].l = l;
tree[rt].r = r;
tree[rt].val = 0;
if (l == r)return;
int mid = (l+r)>>1;
Build(l,mid,rt<<1);
Build(mid+1,r,rt<<1|1);
}
void Updata(int rt , int pos , int val)
{
int l = tree[rt].l;
int r = tree[rt].r;
tree[rt].val += val;
if (l == r)return;
int mid = (l+r)>>1;
if (pos <= mid)Updata(rt<<1,pos,val);
else Updata(rt<<1|1,pos,val);
}
int Query(int rt , int l , int r)
{
if (l == tree[rt].l && r == tree[rt].r)return tree[rt].val;
int mid = (tree[rt].l+tree[rt].r)>>1;
if (r <= mid)
return Query(rt<<1,l,r);
if (l > mid)
return Query(rt<<1|1,l,r);
return Query(rt<<1,l,mid)+Query(rt<<1|1,mid+1,r);
}
int main()
{
int n , m , i , t;
scanf("%d",&t);
while (t--)
{
scanf("%d%d",&n,&m);
memset(vis , 0 , sizeof vis);
for (i = 1 ; i <= n ; i++)
{
scanf("%d",num+i);
pos[num[i]] = i;
}
for (i = 1 ; i <= m ; i++)
{
scanf("%d%d",&tp[i].l,&tp[i].r);
tp[i].val = i;
}
sort(tp+1 , tp+m+1 , cmp);
Build(1,n,1);
int cnt = 1;
ans[1] = ans[2] = -5;
for (i = 1 ; i <= n ; i++)
{
Updata(1,i,1);
vis[num[i]] = 1;
if (vis[num[i]+1])Updata(1,pos[num[i]+1],-1);
if (vis[num[i]-1])Updata(1,pos[num[i]-1],-1);
while (i == tp[cnt].r && cnt <= m)
{
ans[tp[cnt].val] = Query(1,tp[cnt].l,tp[cnt].r);
cnt++;
}
}
for (i = 1 ; i <= m ; i++)printf("%d\n",ans[i]);
}
return 0;
}
树状数组:
#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <map>
#include <vector>
#include <cmath>
#include <stack>
#include <queue>
#include <cstdlib>
#include <algorithm>
using namespace std;
typedef __int64 int64;
typedef long long ll;
#define M 100005
#define mod 1000000007
struct node
{
int l , r , val;
}tp[M];
int n , m , num[M] , pos[M] , ans[M] , sum[M];
bool vis[M];
bool cmp(node a , node b)
{
if (a.r < b.r)return true;
return false;
}
int Lowbit(int x)
{
return x&(-x);
}
void Updata(int pos , int val)
{
while (pos <= n)
{
sum[pos] += val;
pos += Lowbit(pos);
}
}
int Query(int l , int r)
{
int ret = 0;
while (r > 0)
{
ret += sum[r];
r -= Lowbit(r);
}
l--;
while (l > 0)
{
ret -= sum[l];
l -= Lowbit(l);
}
return ret;
}
int main()
{
int i , t;
scanf("%d",&t);
while (t--)
{
scanf("%d%d",&n,&m);
memset(vis , 0 , sizeof vis);
memset(sum , 0 , sizeof sum);
for (i = 1 ; i <= n ; i++)
{
scanf("%d",num+i);
pos[num[i]] = i;
}
for (i = 1 ; i <= m ; i++)
{
scanf("%d%d",&tp[i].l,&tp[i].r);
tp[i].val = i;
}
sort(tp+1 , tp+m+1 , cmp);
int cnt = 1;
for (i = 1 ; i <= n ; i++)
{
Updata(i,1);
vis[num[i]] = 1;
if (vis[num[i]+1])Updata(pos[num[i]+1],-1);
if (vis[num[i]-1])Updata(pos[num[i]-1],-1);
while (i == tp[cnt].r && cnt <= m)
{
ans[tp[cnt].val] = Query(tp[cnt].l,tp[cnt].r);
cnt++;
}
}
for (i = 1 ; i <= m ; i++)printf("%d\n",ans[i]);
}
return 0;
}