HDU4638——Group(树状数组+离线操作)

题目链接

题目大意

n个数的序列,m次询问。

求一段区间连续数字的段数 。

(1 3 5 4 2) 询问[2,4]区间则3,5,4为连续序列输出 1 。

解题思路

我觉得这是一道不错的题目。

定义线段是求的连续序列。

首先将所有的询问离线,按照Li递增排序。

我们可以用一个结构维护Li为起点加入所有点后的各区间线段数,对于每个以Li为起点的询问进行处理。

当然这样不够,我们还要消除Li之前加入的点的影响。

所以我们可以用树状数组或者线段树维护区间的线段数。

利用树状数组维护:树状数组在对于线段的操作没有线段树强大,但是我们可以对于问题进行转化,维护加入某点之后线段数的增减数量,有点类似于扫描线,对与加入新的点x,我们可以看看x-1,x+1是否已经存在。如果都存在就update(pos[x],-1),因为x合并了两条线段。同理存在其中一个update(pos[x],0),否则单独自己成为一条线段就update(pos[x],1)。对于消除x的影响时,只需update(pos[x-1],1),update(pos[x+1],1)。因为如果x对于x+1,或x-1有影响那么说明x+1,x-1一定在x之后加入,去掉x之后它们要+1。

线段树维护的话就直接维护线段数就好了。

#include <iostream>
#include <algorithm>
#include <cmath>
#include <cstring>
#include <cstdio>
#include <cstdlib>
using namespace std;

typedef long long LL;
const int INF = 0x7fffffff;
const int MOD = 1e9 + 7;
const int N = 1e5 + 10;

bool vis[N];
int id[N], c[N], ans[N], pos[N];

struct Interval {
    int l, r, i;
} Q[N];
int cmp(Interval a, Interval b) {
    return a.l < b.l;
}

int lowbit(int x) {
    return x & -x;
}
int update(int i, int v) {
    for( ; i < N; i += lowbit(i)) {
        c[i] += v;
    }
}
int query(int i) {
    int res = 0;
    for(; i; i -= lowbit(i)) {
        res += c[i];
    }
    return res;
}
int getsum (int l, int r) {
    return query(r) - query(l - 1);
}

int main() {
#ifdef Tally_Ho
    freopen("in.txt", "r", stdin);
#endif // Tally_Ho
    int T, n, m;
    scanf("%d", &T);
    while(T--) {
        scanf("%d%d", &n, &m);
        memset(c, 0, sizeof(c));
        memset(vis, 0, sizeof(vis));
        for(int i = 1; i <= n; i++) {
            scanf("%d", &id[i]);
            pos[id[i]] = i;
        }
        for(int i = 0; i < m; i++) {
            int l, r;
            scanf("%d%d", &Q[i].l, &Q[i].r);
            Q[i].i = i;
        }
        sort(Q, Q + m, cmp);
        for(int i = 1; i <= n; i++) {
            vis[id[i]] = 1;
            if(vis[id[i] - 1] && vis[id[i] + 1]) {
                update(i, -1);
            } else if(vis[id[i] - 1] || vis[id[i] + 1]) {
                update(i, 0);
            } else {
                update(i, 1);
            }
        }
        int cur = 1;
        for(int i = 0; i < m; i++) {
            int L = Q[i].l, R = Q[i].r;
            while(cur < L) {
                if(id[cur] != 1) update(pos[id[cur] - 1], 1);
                if(id[cur] != n) update(pos[id[cur] + 1], 1);
                cur++;
            }
            ans[Q[i].i] = getsum(L, R);
        }
        for(int i = 0; i < m; i++) {
            printf("%d\n", ans[i]);
        }
    }
    return 0;
}
时间: 2024-08-03 10:37:41

HDU4638——Group(树状数组+离线操作)的相关文章

hdu 4630 树状数组+离线操作+GCD

http://acm.hdu.edu.cn/showproblem.php?pid=4630 重新认识了树状数组. 首先要记住那个树形的图,然后+或-lowbit(i)是自己根据具体问题设定的,不要死于+或者-, 树状数组的特点: 1.+lowbit(i)可以到达包含结点i的上一层父节点    所以用于值的更改 2.-lowbit(i)可以到达不包含i所代表区间的上一层父节点  所以用于值的求和---每个不相交的段加起来 3.C[i]的含义也是根据具体问题去做设定的,但是c[i]覆盖了a[i-2

POJ2985 The k-th Largest Group[树状数组求第k大值 并查集]

The k-th Largest Group Time Limit: 2000MS   Memory Limit: 131072K Total Submissions: 8807   Accepted: 2875 Description Newman likes playing with cats. He possesses lots of cats in his home. Because the number of cats is really huge, Newman wants to g

HDU---4417Super Mario 树状数组 离线操作

题意:给定 n个数,查询 位置L R内 小于x的数有多少个. 对于某一次查询 把所有比x小的数 ”的位置“ 都加入到树状数组中,然后sum(R)-sum(L-1)就是答案,q次查询就要离线操作了,按高度排序. #include <set> #include <map> #include <cmath> #include <ctime> #include <queue> #include <stack> #include <cct

hdu 2985 The k-th Largest Group 树状数组求第K大

The k-th Largest Group Time Limit: 2000MS   Memory Limit: 131072K Total Submissions: 8353   Accepted: 2712 Description Newman likes playing with cats. He possesses lots of cats in his home. Because the number of cats is really huge, Newman wants to g

Codeforces 369E Valera and Queries --树状数组+离线操作

题意:给一些线段,然后给m个查询,每次查询都给出一些点,问有多少条线段包含这个点集中的一个或多个点 解法:直接离线以点为基准和以线段为基准都不好处理,“正难则反”,我们试着求有多少线段是不包含某个查询的任意一个点的.这时候我们可以建立点集的补集,以线段的形式,如果点集的补集线段包含了某条给出的线段,那么被包含的那条线段肯定不会包括任意一个点,那么该组查询的答案ans--即可. 用树状数组做,离线读入数据,按容易被包含的线段优先排个序,然后扫一遍,边统计边修改即可. 代码: #include <i

HDU 4630 No Pain No Game 树状数组+离线操作

题意:给一串数字,每次查询[L,R]中两个数的gcd的最大值. 解法:容易知道,要使取两个数让gcd最大,这两个数最好是倍数关系,所以处理出每个数的所有倍数,两两间根据倍数关系形成一条线段,值为该数.那么每次查询[L,R]之间两数gcd的最大值即为查询[L,R]中值最大的线段,离线所有的查询数据,然后按右端点坐标从小到大排序,依次往右加入即可. 这里学到了树状数组维护最大值的写法. 代码: #include <iostream> #include <cstdio> #include

hdu 3333 Turing Tree(树状数组离线操作)

Turing Tree Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Total Submission(s): 3904    Accepted Submission(s): 1325 Problem Description After inventing Turing Tree, 3xian always felt boring when solving problems

SPOJ3267--D-query (树状数组离线操作)

题意查询区间 [l,r]内有多少个不同的数字 先把所有询问按 右端点进行排序,然后离线操作.如果该位置的数字 已经出现过那么把前一个位置-1,当前位置+1.扫一遍输出. 1 #include <cstdio> 2 #include <string> 3 #include <vector> 4 #include <cstdlib> 5 #include <cstring> 6 #include <algorithm> 7 using n

hdu 3874 Necklace (树状数组+离线操作)

Necklace Time Limit: 15000/5000 MS (Java/Others)    Memory Limit: 65536/32768 K (Java/Others)Total Submission(s): 3923    Accepted Submission(s): 1292 Problem Description Mery has a beautiful necklace. The necklace is made up of N magic balls. Each b