0
点赞
收藏
分享

微信扫一扫

HDU 4638 Group 树状数组离线


题目: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;
}



举报

相关推荐

0 条评论