题目:http://acm.hdu.edu.cn/showproblem.php?pid=4638
题意:给定一个长度为n的数组,数组中元素为1到n,询问某个区间内有多少段连续的数字
思路:之前用莫队算法写的,很简单,今天用树状数组离线搞得,感觉不如莫队好想,但是效率高了将近一倍。把询问按照右端点从小到大排序,对于每个数,我们看成是孤立的,更新到树状数组上,即把这个位置加1,然后判断这个数的左右相邻数字是否已经更新到树状数组中,若已更新则去掉,那么求某个区间内连续数字的段数变转换为求区间和
#include <iostream>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
using namespace std;
typedef long long ll;
const int N = 100010;
int bit[N], res[N];
int a[N], b[N], pos[N];
bool vis[N];
int n, m;
struct node
{
int l, r, id;
} g[N];
void add(int i, int x)
{
while(i <= n) bit[i] += x, i += i & -i;
}
int sum(int i)
{
ll s = 0;
while(i > 0) s += bit[i], i -= i & -i;
return s;
}
bool cmp(node a, node b)
{
return a.r < b.r;
}
int main()
{
int t;
scanf("%d", &t);
while(t--)
{
scanf("%d%d", &n, &m);
memset(bit, 0, sizeof bit);
memset(vis, 0, sizeof vis);
for(int i = 1; i <= n; i++) scanf("%d", &a[i]), pos[a[i]] = i;
for(int i = 1; i <= m; i++) scanf("%d%d", &g[i].l, &g[i].r), g[i].id = i;
sort(g+1, g+1+m, cmp);
int tot = 1;
for(int i = 1; i <= n; i++)
{
add(i, 1);//把每一个数都看成孤立的,故+1
//若这个数的左右相近数字已经更新到树状数组中,要对应的-1
if(a[i] - 1 >= 1 && pos[a[i]-1] < i) add(pos[a[i]-1], -1);
if(a[i] + 1 <= n && pos[a[i]+1] < i) add(pos[a[i]+1], -1);
while(tot <= m && g[tot].r == i)
res[g[tot].id] = sum(g[tot].r) - sum(g[tot].l - 1), tot++;
}
for(int i = 1; i <= m; i++) printf("%d\n", res[i]);
}
return 0;
}