题意:
给你一个1~n的排列,问区间[l, r] 之间有多少段连续的数,比如排列5 1 3 2 4,询问[1, 3]结果为3,询问[2, 4]结果为1
这题也是挺不错的。解题思路:
首先应该想到要对所有询问离线处理,先预处理好[1, i]的结果,对所有询问按照L从小到大排个序,然后讲左边一个点一个点删除,每删除一个点,必然对后面的结果产生影响,假设要删除的点的初始值为x,如果x-1和x+1在后面,可以很容易知道删除x并不会对x-1和x+1之间的询问值造成影响,而对于左边的应该是少了1,对于后边的多了1。
如果x-1和x+1都在x左边,那x就没有相邻的数在右边了,那原来的情况x就算是一个连续块了,那么删除x后,右边的询问值应该全部少了1。
如果x-1和x+1有一个在右边,那么对于这个位置的右边是不影响的,左边都少了1。
具体见代码。
#include
#include
#include
using namespace std;
#define lowbit(x) ((x)&(-x))
const int maxn = 100005;
struct PP{
int l , r, id;
bool operator < (const PP &a) const {
return l < a.l;
}
}q[maxn];
int n, a[maxn], d[maxn], node[maxn], pos[maxn], print[maxn];
bool vis[maxn];
void add(int x, int val) {
while(x <= n) {
node[x] += val;
x += lowbit(x);
}
}
int get_sum(int x) {
int ret = 0;
while(x > 0) {
ret += node[x];
x -= lowbit(x);
}
return ret;
}
int main() {
int i, t, m;
scanf("%d", &t);
while(t--) {
scanf("%d%d", &n, &m);
vis[0] = vis[n+1] = 0;
for(i = 1;i <= n; i++) {
scanf("%d", &a[i]);
vis[i] = node[i] = 0;
pos[a[i]] = i;
}
d[1] = 1;
vis[a[1]] = 1;
for(i = 2;i <= n; i++) {
d[i] = d[i-1];
if(vis[a[i]-1] && vis[a[i]+1]) d[i] --;
else if(!vis[a[i]-1] && !vis[a[i]+1]) d[i]++;
vis[a[i]] = 1;
}
// for(i = 1;i <= n ;i++) printf("%d ", d[i]); puts("");
for(i = 1;i <= n ;i++) {
add(i, d[i]);
add(i+1, -d[i]);
}
for(i = 0;i < m; i++)
scanf("%d%d", &q[i].l, &q[i].r), q[i].id = i;
sort(q, q+m);
int cur = 1;
for(i = 0;i < m; i++) {
while(cur < q[i].l) {
int now = a[cur];
if(pos[now-1] > cur && pos[now+1] > cur) {
int mx = max(pos[now-1], pos[now+1]);
int mi = min(pos[now-1], pos[now+1]);
add(cur, -1);
add(mi, 1);
add(mx, 1);
}
else {
int mx = max(pos[now-1], pos[now+1]);
if(mx > cur) {
add(cur+1, -1);
add(mx, 1);
}
else
add(cur+1, -1);
}
cur++;
}
print[q[i].id] = get_sum(q[i].r);
}
for(i = 0;i < m; i++)
printf("%d\n", print[i]);
}
return 0;
}