Little Elephant and Array 线段树

题目:http://codeforces.com/problemset/problem/220/B

题意

给定一组数据,多次询问区间内某数字出现次数与该数字数值相同的数的个数

思路

一看到区间查询,就会想到线段树,有木有!

单点或区间的修改、查询等可是线段树的强项嘞√

而我们今天的线段树类型为: 离线处理、区间更新、单点查询

给定一个数字序列,线段树每个节点所要记录的状态是 :从此处以及到正在处理的元素处满足题意的ans

上面一句话什么意思嘞

我们做的是 边建树,边查询,当对应状态的线段树建立完成,即可得到答案。

比如  一段序列 : 8 3 4 4 1 3 3 2 2

当我们处理到第7个数的时候,状态分别为 2 2 1 1 1 0 0

以上是线段树的属性介绍,下面就是具体的操作流程

我们需要知道不同数字所出现的所有的位置,用一个二维空间pos存储该信息,比如上面pos[3]存储的数据即为 2、6、7

首先,将所有的询问区间按照右端点进行非递减排序

我们从序列的第一数字开始,建树。

当num数字第 t 次出现时候,如果,t >= num的时候,我们将【1,num第一次出现的位置】的值+1,

                                             如果,t > num  的时候,我们将【1,num第一次出现的位置】的值-1

以此来保证线段树每个节点所要记录的状态

如此做,当处理到第i个数字的时候,查看是否到达当前正在处理的区间的右端点,

如果到达,那么,当前处理区间的左端点的节点状态即为所求

为什么呢,因为我们是一个一个将序列中的元素放入树中的,而我们的询问区间是按照右端点非递减排序的,那么就保证了,处理到第i个序列元素的时候,到达了询问区间右端点,而根据树节点所记录的状态可知,即为答案,而此时的状态,并没有把i处之后的序列元素作考虑,所以,按照区间右端点递增,且序列元素逐一入树,边建树边查询,树节点所记录的状态定为答案。

为了方便,我们将每种数字第一次出现的位置均赋值为0

代码如下:

#include<iostream>
#include<algorithm>  
#include<vector>  
using namespace std;

const int MAXN = 100005;
int N, M, list[MAXN], s[MAXN], t[MAXN], rank_[MAXN], result[MAXN];
int Tr[MAXN << 2], mark[MAXN << 2];
vector<int> pos[MAXN];

inline bool cmp(const int a, const int b)
{
    return t[a] < t[b];
}

void PushDown(int idx);            //向下更新       沿para向下更新子树

void Update(int idx, int L, int R, int l, int r, int c);     //区间更新    para1:当前结点的树下标

int Query(int idx, int L, int R, int x);                     //单点询问

int main()
{
    cin >> N >> M;
    for (int i = 1; i <= N; i++)
    {
        cin >> list[i];
        if (list[i] <= N && !pos[list[i]].size())
            pos[list[i]].push_back(0);
    }
    for (int i = 0; i < M; i++)
    {
        cin >> s[i] >> t[i];
        rank_[i] = i;
    }
    sort(rank_, rank_ + M, cmp);
    for (int i = 1, j = 0; i <= N && j < M; i++)
    {
        if (i == 7)
            int s = 0;
        if (list[i] <= N)
        {
            pos[list[i]].push_back(i);
            if (pos[list[i]].size() > list[i])
                Update(1, 1, N, pos[list[i]][pos[list[i]].size() - list[i] - 1] + 1, pos[list[i]][pos[list[i]].size() - list[i]], 1);
            if (pos[list[i]].size() > list[i] + 1)
                Update(1, 1, N, pos[list[i]][pos[list[i]].size() - list[i] - 2] + 1, pos[list[i]][pos[list[i]].size() - list[i] - 1], -1);
        }
        for (; t[rank_[j]] == i && j < M; j++)
            result[rank_[j]] = Query(1, 1, N, s[rank_[j]]);
    }
    for (int i = 0; i < M; i++)
        printf("%d\n", result[i]);
    return 0;
}

void PushDown(int idx)
{
    int left = idx << 1, right = (idx << 1) ^ 1;
    Tr[left] += mark[idx];
    mark[left] += mark[idx];
    Tr[right] += mark[idx];
    mark[right] += mark[idx];
    mark[idx] = 0;
}

void Update(int idx, int L, int R, int l, int r, int c)
{
    if (l <= L && R <= r)
    {
        Tr[idx] += c;
        mark[idx] += c;
        return;
    }
    if (mark[idx])
        PushDown(idx);
    int mid = (L + R) >> 1, left = idx << 1, right = (idx << 1) ^ 1;
    if (l <= mid)
        Update(left, L, mid, l, r, c);
    if (mid < r)
        Update(right, mid + 1, R, l, r, c);
}

int Query(int idx, int L, int R, int x)
{
    if (x == L & R == x)
        return Tr[idx];
    if (mark[idx])
        PushDown(idx);
    int mid = (L + R) >> 1, left = idx << 1, right = (idx << 1) ^ 1;
    if (x <= mid)
        return Query(left, L, mid, x);
    else
        return Query(right, mid + 1, R, x);
}

当然,作为线段树的好朋友,树状数组当然也可以做喽

见:https://blog.csdn.net/u011026968/article/details/38589907

感谢您的阅读,生活愉快~

猜你喜欢

转载自www.cnblogs.com/lv-anchoret/p/9063115.html