Yassin 最近在量化投资方面很有兴趣。
为了研究哪只股票是真正的牛股,他把历史 nn 天每一天成交量最大的股票代码写成了一排,并构建了一套属于自己的“理论体系”。
成交量多说明人气好,人气好的肯定买的人多,赚钱就要靠人气! – Yassin
但是知道的人太多,这个大家都去接盘,那就都成为韭菜了 – Makik
基于这个理论,Yassin 想知道 [L, R] 区间中人气“比较”好的股票有哪些,具体而言,他会给定你 L, R, k,你则需要告诉他 [L, R] 中出现 k 次的股票有多少只。
例如 n = 5 时,假设这个代码序列为 {1, 1, 2, 3, 1} 现在他给了一个询问 (2, 5, 1),你就需要回答他 {a2,a3,a4,a5} 中恰好出现 1 次的有多少种元素。答案显然为 2,恰好出现一次的元素为 2,3。
数据保证 ai > 0, k <= n <= 4e4, q <= 4e4, l <= r。
Input
第一行 2 个数字 n, q 分别表示序列的长度和询问的个数。
接下来一行 n 个数字,为序列 {an}
接下来 q 行,每行三个数字 L, R, k, 如题目描述所述。
Output
共 q 行,每行一个数字,为对应询问的答案。
Example
5 1
1 1 2 3 1
2 5 1
2
Hint
本题目描述中所涉及到的投资知识仅作为娱乐,不作为投资建议。
股市有风险,入市需谨慎。
输入n,q。 表示一共有n 个数,q次询问,接下来输入n个数字,a1 … an, 对于每个询问,输入L, R, K,输出[L, R]区间内恰好出现K次的数的个数是多少。
题意很明显是一道数据结构题,区间查询,并且不修改只询问,对于这类区间问题,此时可以使用莫队算法,对于每个询问分快排序,离线后处理每个询问即可。复杂度O(n√n)。
莫队算法维护两个数组,一个是cnt,cnt[x] 表示x出现了几次,res[x] 表示恰好出现了x次的数量,然后每个询问双指针移动add or del即可。
#include
using namespace std;
typedef long long LL;
typedef pair<int, int> PII;
const int N = 1e5 +50;
int n, m;
int a[N], belong[N], ans[N], cnt[N], res[N];
struct node {
int l, r, k, id;
} q[N];
bool cmp(node a, node b) {
return belong[a.l] ^ belong[b.l] ? belong[a.l] < belong[b.l] : ((belong[a.l] & 1) ? a.r < b.r : a.r > b.r);
}
void del(int x) {
res[cnt[x]] --;
cnt[x] --;
res[cnt[x]] ++;
}
void add(int x) {
res[cnt[x]] --;
cnt[x] ++;
res[cnt[x]] ++;
}
int main() {
ios::sync_with_stdio(false);
cin >> n >> m;
double size = sqrt(n);
int num = ceil(n / size);
for (int i = 1; i <= num; i ++)
for (int j = (i - 1) * size + 1; j <= i * size; j ++)
belong[j] = i;
for (int i = 1; i <= n; i ++) cin >> a[i];
for (int i = 1; i <= m; i ++) {
cin >> q[i].l >> q[i].r >> q[i].k;
q[i].id = i;
}
sort(q + 1, q + 1 + m, cmp);
int l = 1, r = 0;
for (int i = 1; i <= m; i ++) {
int ql = q[i].l, qr = q[i].r, k = q[i].k;
while (l < ql) del(a[l ++]);
while (l > ql) add(a[-- l]);
while (r < qr) add(a[++ r]);
while (r > qr) del(a[r --]);
ans[q[i].id] = res[k];
}
for (int i = 1; i <= m; i ++) cout << ans[i] << endl;
return 0;
}