标签:article lin https color tps details oid code ios
题目: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
感谢您的阅读,生活愉快~
标签:article lin https color tps details oid code ios
原文地址:https://www.cnblogs.com/lv-anchoret/p/9063115.html