Luogu P4396 [AHOI2013]作业

发布时间 2023-06-05 16:57:10作者: 觉清风

[AHOI2013]作业

题目描述

此时己是凌晨两点,刚刚做了 Codeforces 的小 A 掏出了英语试卷。英语作业其实不算多,一个小时刚好可以做完。然后是一个小时可以做完的数学作业,接下来是分别都是一个小时可以做完的化学,物理,语文……小 A 压力巨大。

这时小 A 碰见了一道非常恶心的数学题,给定了一个长度为 \(n\) 的数列和若干个询问,每个询问是关于数列的区间表示数列的第 \(l\) 个数到第 \(r\) 个数),首先你要统计该区间内大于等于 \(a\),小于等于 \(b\) 的数的个数,其次是所有大于等于 \(a\),小于等于 \(b\) 的,且在该区间中出现过的数值的个数。

小 A 望着那数万的数据规模几乎绝望,只能向大神您求救,请您帮帮他吧。

输入格式

第一行两个整数 \(n,m\)

接下来 \(n\) 个不超过 \(10^5\) 的正整数表示数列

接下来 \(m\) 行,每行四个整数 \(l,r,a,b\),具体含义参见题意。

输出格式

输出 \(m\) 行,分别对应每个询问,输出两个数,分别为在 \(l\)\(r\) 这段区间中大小在 \([a,b]\) 中的数的个数,以及大于等于 \(a\),小于等于 \(b\) 的,且在该区间中出现过的数值的个数(具体可以参考样例)。

样例 #1

样例输入 #1

3 4
1 2 2
1 2 1 3
1 2 1 1
1 3 1 3
2 3 2 3

样例输出 #1

2 2
1 1
3 2
2 1

提示

\(N\leq 100000,M\leq 100000\),读入的数字均为 \([1,10^5]\) 内的正整数。

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>

using namespace std;

int n, m, block, size_block, l = 1, r = 0;
int maxn;
int a[5211314], pos[5211314];
int cnt[5211314], size_add[5211314], size_cnt[5211314];
//此处的块指数的大小分成的块,而不是数的编号分成的块
//size_add表示在第i个块的数的个数 size_cnt指第i个块出现过的数值的个数
int out_cnt[5211314], out_sum[5211314];

struct ASK {
	int l, r, a, b, id;
}ask[5211314];

inline int read() {
	int x = 0, f = 1;
	char ch = getchar();
	while (ch < '0' || ch > '9') {
		if (ch == '-') f = -1;
		ch = getchar();
	}
	while (ch >= '0' && ch <= '9') {
		x = (x << 1) + (x << 3) + (ch - '0');
		ch = getchar();
	}
	return x * f;
}

inline bool cmp(ASK a, ASK b) {
	if (pos[a.l] != pos[b.l]) return pos[a.l] < pos[b.l];
	else return a.r < b.r;
}

inline void Add(int number) {
	cnt[number] ++;
	size_add[pos[number]] ++;
	if (cnt[number] == 1) {
		size_cnt[pos[number]] ++;
	}
	return;
}

inline void Del(int number) {
	cnt[number] --;
	size_add[pos[number]] --;
	if (cnt[number] == 0) {
		size_cnt[pos[number]] --;
	}
	return;
}

inline void GetAns(int minn, int maxn, int k) {
	int lift = pos[minn], right = pos[maxn];
	for (int i = minn; i <= min(lift * block, maxn); ++ i) {
		out_sum[k] += cnt[i];
		if (cnt[i] != 0) out_cnt[k] ++;
	}
	if (lift != right) {
		for (int i = (right - 1) * block + 1; i <= maxn; ++ i) {
			out_sum[k] += cnt[i];
			if (cnt[i] != 0) out_cnt[k] ++;
		}
	}
	for (int i = lift + 1; i <= right - 1; ++ i) {
		out_sum[k] += size_add[i];
		out_cnt[k] += size_cnt[i];
	}
}

int main() {
	n = read();
	m = read();
	block = sqrt(n);
	for (int i = 1; i <= n; ++ i) {
		a[i] = read();
		pos[i] = (i - 1) / block + 1;
		//pos[i]表示值i在第几个块
	}
	for (int i = 1; i <= m; ++ i) {
		ask[i].l = read();
		ask[i].r = read();
		ask[i].a = read();
		ask[i].b = read();
		ask[i].id = i;
	}
	stable_sort(ask + 1, ask + m + 1, cmp);
	for (int i = 1; i <= m; ++ i) {
		while (r < ask[i].r) Add(a[++ r]);	
		while (r > ask[i].r) Del(a[r --]);
		while (l > ask[i].l) Add(a[-- l]);
		while (l < ask[i].l) Del(a[l ++]);
		GetAns(ask[i].a, ask[i].b, ask[i].id);
	}
	for (int i = 1; i <= m; ++ i) {
		printf("%d %d\n", out_sum[i], out_cnt[i]);
	}
	return 0;
}