bzoj 2038 [2009国家集训队]小Z的袜子(hose) 莫队

题面

题目传送门

解法

可以说是莫队裸题了吧

莫队算法的本质是,将所有询问的区间按照一定顺序排序,然后不断通过已知区间更新后面的区间

假设现在已经做到区间\([l_1,r_1]\),下一个区间是\([l_2,r_2]\)

那么就暴力将区间从\([l_1,r_1]\)扩展至\([l_2,r_2]\),并在扩展的时候更新答案

更新答案对于此题比较简单,直接\(O(1)\)计算即可

每一次更新的复杂度为\(O(|l_1-l_2|+|r_1-r_2|)\)

所以我们要以一个比较适当的顺序,使得这个加起来的总和尽量小

我排序的方式比较鬼畜,是看某位卡常dalao写的

时间复杂度:\(O(n\sqrt m)\)

代码

#include <bits/stdc++.h>
#define int long long
#define N 50010
using namespace std;
template <typename node> void read(node &x) {
	x = 0; int f = 1; char c = getchar();
	while (!isdigit(c)) {if (c == '-') f = -1; c = getchar();}
	while (isdigit(c)) x = x * 10 + c - '0', c = getchar(); x *= f;
}
struct Node {
	int l, r, id;
} b[N];
struct Ans {
	int x, y;
} ans[N];
int sum, a[N], s[N], col[N];
bool cmp(Node x, Node y) {
	return col[x.l] ^ col[y.l] ? col[x.l] < col[y.l] : col[x.l] & 1 ? x.r < y.r : x.r > y.r;
}
void update(int x, int v) {
	sum += 2 * s[a[x]] * v - v + v * v;
	s[a[x]] += v;
}
main() {
	int n, q; read(n), read(q);
	for (int i = 1; i <= n; i++) read(a[i]);
	for (int i = 1; i <= q; i++)
		read(b[i].l), read(b[i].r), b[i].id = i;
	int siz = n / sqrt(q * 2 / 3);
	for (int i = 1; i <= q; i++) col[i] = (i + siz - 1) / siz;
	sort(b + 1, b + q + 1, cmp);
	for (int i = 1, l = 1, r = 0; i <= q; i++) {
		while (r < b[i].r) update(++r, 1);
		while (r > b[i].r) update(r--, -1);
		while (l < b[i].l) update(l++, -1);
		while (l > b[i].l) update(--l, 1);
		if (!sum) {ans[b[i].id] = (Ans) {0, 1}; continue;}
		int x = sum, y = (b[i].r - b[i].l + 1) * (b[i].r - b[i].l);
		int t = __gcd(x, y); ans[b[i].id] = (Ans) {x / t, y / t};
	}
	for (int i = 1; i <= q; i++) cout << ans[i].x << '/' << ans[i].y << "\n";
	return 0;
}

posted @ 2018-08-14 23:15  谜のNOIP  阅读(111)  评论(0编辑  收藏  举报