离线处理 + 树状数组 洛谷1972

本文介绍了一种使用离线处理和树状数组优化查询的方法,通过低字节(lowbit)函数更新树状数组,处理区间内的贝壳有效状态,以解决特定类型的查询问题。在处理每个查询时,更新贝壳的状态并计算当前查询的答案,确保每个查询的答案正确无误。这种方法在处理大量查询时能提高效率。
摘要由CSDN通过智能技术生成

1.离线处理的含义是将查询全部处理之后在输出结果。我们用到排序来简化操作,使得我们不能边处理边输出答案。

2.要熟知树状数组的更新,查询,lowbit等基本操作。

洛谷1972

现附上代码

#include<stdio.h>
#include<algorithm>
using namespace std;
const int N = 1e6 + 5;

int v[N], t[N], ans[N], a[N];

struct query
{
    int l, r;
    int pos;
} q[N];


bool cmp(query x, query y)
{
    return x.r < y.r;
}

int lowbit(int x)
{
    return x & (-x);
}

void update(int x, int d)
{
    while (x <= N)
    {
        t[x] += d;
        x += lowbit(x);
    }
}

int sum(int x)
{
    int ans = 0;
    while (x > 0)
    {
        ans += t[x];
        x -= lowbit(x);
    }
    return ans;
}
int main()
{
    int n;
    scanf("%d", &n);
    for (int i = 1; i <= n; i++)
    {
        scanf("%d", &a[i]);
    }
    int m;
    scanf("%d", &m);
    for (int i = 1; i <= m; i++)
    {
        scanf("%d %d", &q[i].l, &q[i].r);
        q[i].pos = i;
    }
    sort(q + 1, q + m + 1, cmp);
    int nex = 1;
    for (int i = 1; i <= m; i++)
    {
        for (int j = nex; j <= q[i].r; j++)
        {
            if (v[a[j]])
            {
                update(v[a[j]], -1);
            }
            update(j, 1);
            v[a[j]] = j;
        }
        nex = q[i].r + 1;
        ans[q[i].pos] = sum(q[i].r) - sum(q[i].l - 1);
    }
    for (int i = 1; i <= m; i++)
        printf("%d\n", ans[i]);
    return 0;
}

首先要注意理解一下以下几个关键点

1.v数组用来记录一种贝壳在之前有没有出现过,并记录最后一次出现的下标,a数组用来存放贝壳的种类,ans数组用来存放每个查询的结果,t数组是树状数组,用来存放当前需要考虑的贝壳。

2.显然,我们只需要考虑相同出现的贝壳中最右边的那一个就行了。当我们每处理完一个查询时, 得到的t数组可以通过求前缀和的方式得到这个查询的答案。

举个例子

用样例来说明,n = 6, 1, 2, 3, 4, 3, 5

考虑特殊情况区间[3, 5], 当考察a[3] = 3时,可以发现之前并没有出现过3,因此是有效的,所有的贝壳的有效值为0, 0 ,1, 0, 0,而很关键的,树状数组可以很快的求出该数组的前缀和,它的前缀和就是区间查询的答案。在考察a[4]  = 4, 所有贝壳的有效值为0, 0, 1, 1, 0, 0;在考察a[5] = 3, 在下标为3的位置出现过了,因此在这个区间里,a[3] = 3变成了无效值,a[5] = 3是有效值,所有贝壳的有效值为0, 0, 0, 1, 1, 0,得到sum(5) - sum(2) = 2;

还要注意的是每处理一个查询,左边在查询区间外的贝壳如果与查询区间内有相同的是会变为无效的,因此得处理完一个查询就要保存一个查询的答案。

举个例子

和上面一样的先处理[1, 3]区间,得到1,1,1, 0, 0,0,在处理[5, 6]区间,得到1,1,0,1,1,1,现在再去得到[1,3]区间的结果为2, 但这是错误的因此每处理完一个区间就应该保存一个答案。

如有不对的地方,欢迎指出。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值