bzoj4241 历史研究 分块

题目传送门

https://lydsy.com/JudgeOnline/problem.php?id=4241

题解

这道题要求出来的就是一个区间带权众数。

回忆一下一般的区间众数怎么求:

分块,对于每一次询问 \(l, r\),如果 \(l, r\) 不在同一个整块中,那么答案一定是中间的整块的众数,或者两旁的剩下来的数。

这个东西可以预处理一个 \(f[i][j]\) 表示从第 \(i\) 块到 \(j\) 的众数就可以单次 \(\sqrt n\) 求出了。

对于两旁的剩数,每个数可以预处理每个值的出现位置的序列,在上面二分求出每个数的出现次数。


可以发现上面的结论依然可以用于带权众数。

不过这道题的数据范围大了一点,有 \(10^5\),所以需要 \(O(1)\) 求出一个数在某一整段中的出现次数。因为是整段,也就是开头到结尾包含的都是整块,所以可以预处理一个前缀和 \(s[i][j]\) 表示前 \(i\) 块中 \(j\) 的出现次数就可以求出来了。


伤心,\(O((n+q)\sqrt n)\)\(O((n+q)\sqrt n \log n)\) 吊打。

#include<bits/stdc++.h>

#define fec(i, x, y) (int i = head[x], y = g[i].to; i; i = g[i].ne, y = g[i].to)
#define dbg(...) fprintf(stderr, __VA_ARGS__)
#define File(x) freopen(#x".in", "r", stdin), freopen(#x".out", "w", stdout)
#define fi first
#define se second
#define pb push_back

template<typename A, typename B> inline char smax(A &a, const B &b) {return a < b ? a = b, 1 : 0;}
template<typename A, typename B> inline char smin(A &a, const B &b) {return b < a ? a = b, 1 : 0;}

typedef long long ll; typedef unsigned long long ull; typedef std::pair<int, int> pii;

template<typename I> inline void read(I &x) {
	int f = 0, c;
	while (!isdigit(c = getchar())) c == '-' ? f = 1 : 0;
	x = c & 15;
	while (isdigit(c = getchar())) x = (x << 1) + (x << 3) + (c & 15);
	f ? x = -x : 0;
}

const int N = 1e5 + 7;
const int B = 316 + 7;

#define bl(x) (((x) - 1) / blo + 1)
#define st(x) (((x) - 1) * blo + 1)
#define ed(x) std::min((x) * blo, n)

int n, dis, Q, blo;
int a[N], b[N], s[B][N], cnt[N];
ll f[B][N];

inline void ycl() {
	for (int i = 1; i <= bl(n); ++i)
		for (int j = st(i); j <= ed(i); ++j) ++s[i][a[j]];
	for (int i = 1; i <= bl(n); ++i)
		for (int j = 1; j <= dis; ++j) s[i][j] += s[i - 1][j];
	for (int i = 1; i <= bl(n); ++i) {
		memset(cnt, 0, sizeof(int) * (dis + 1));
		int x = st(i);
		ll mx = 0;
		for (int j = x; j <= n; ++j) {
			smax(mx, (ll)b[a[j]] * ++cnt[a[j]]);
			f[i][j] = mx;
		}
	}
	memset(cnt, 0, sizeof(int) * (dis + 1));
}

inline ll qry(int l, int r) {
	ll ans = 0;
	if (bl(l) == bl(r)) {
		for (int i = l; i <= r; ++i) smax(ans, (ll)b[a[i]] * ++cnt[a[i]]);
		for (int i = l; i <= r; ++i) cnt[a[i]] = 0;
		return ans;
	}
	ans = f[bl(l) + 1][r];
	int sl = bl(l) + 1, sr = bl(r) - 1;
	for (int i = l; i <= ed(bl(l)); ++i) smax(ans, (ll)b[a[i]] * (++cnt[a[i]] + s[sr][a[i]] - s[sl - 1][a[i]]));
	for (int i = st(bl(r)); i <= r; ++i) smax(ans, (ll)b[a[i]] * (++cnt[a[i]] + s[sr][a[i]] - s[sl - 1][a[i]]));
	for (int i = l; i <= ed(bl(l)); ++i) cnt[a[i]] = 0;
	for (int i = st(bl(r)); i <= r; ++i) cnt[a[i]] = 0;
	return ans;
}

inline void lsh() {
	std::sort(b + 1, b + n + 1);
	dis = std::unique(b + 1, b + n + 1) - b - 1;
	for (int i = 1; i <= n; ++i) a[i] = std::lower_bound(b + 1, b + dis + 1, a[i]) - b;
}

inline void work() {
	lsh();
	ycl();
	while (Q--) {
		int l, r;
		read(l), read(r);
		printf("%lld\n", qry(l, r));
	}
}

inline void init() {
	read(n), read(Q);
	blo = sqrt(n);
	for (int i = 1; i <= n; ++i) read(a[i]), b[i] = a[i];
}

int main() {
#ifdef hzhkk
	freopen("hkk.in", "r", stdin);
#endif
	init();
	work();
	fclose(stdin), fclose(stdout);
	return 0;
}
posted @ 2019-10-16 21:51  hankeke  阅读(80)  评论(0编辑  收藏  举报