Bzoj2038/洛谷P1494 小Z的袜子(莫队)

题面

Bzoj

洛谷

题解

考虑莫队算法,首先对询问进行分块(分块大小为$sqrt(n)$),对于同一个块内的询问,按照左端点为第一关键字,右端点为第二关键字排序。我们统计这个区间内相同的颜色有多少个,假设某种颜色$i$有$j$个,则贡献就是$j\times(j-1)$,最后记得化成既约分数。

#include <set>
#include <cmath>
#include <cstdio>
#include <cstring>
#include <algorithm>
using std::min; using std::max;
using std::swap; using std::sort;
typedef long long ll;
using std::set;

template<typename T>
void read(T &x) {
	int flag = 1; x = 0; char ch = getchar();
	while(ch < '0' || ch > '9') { if(ch == '-') flag = -flag; ch = getchar(); }
	while(ch >= '0' && ch <= '9') x = x * 10 + ch - '0', ch = getchar(); x *= flag;
}

const int N = 5e4 + 10, M = 5e4 + 10;
int n, m, c[N], siz, blo, t, l, r, v[N];
struct Ques { int x, y, z; } q[M], b[M];
inline bool cmp1 (Ques a, Ques b) { return a.x < b.x; }
inline bool cmp2 (Ques a, Ques b) { return a.y < b.y; }
ll up[M], down[M], _gcd, tmp;
template<typename T>
T gcd(T a, T b) { return b ? gcd(b, a % b) : a; }

void modify (int st, int ed, int flag) {
	for(int i = st; i < ed; ++i) {
		tmp -= 1ll * v[c[i]] * (v[c[i]] - 1);
		v[c[i]] += flag;
		tmp += 1ll * v[c[i]] * (v[c[i]] - 1);
	}
}

int main () {
#ifdef OFFLINE_JUDGE
    freopen("233.in", "r", stdin);
    freopen("233.out", "w", stdout);
#endif
	read(n), read(m);
	for(int i = 1; i <= n; ++i) read(c[i]);
	for(int i = 1; i <= m; ++i)
		read(q[i].x), read(q[i].y), q[i].z = i;
	sort(&q[1], &q[m + 1], cmp1);
	siz = sqrt(n), blo = (n / siz) + (n % siz != 0);
	for(int i = 0, j = 1; i < blo; ++i) {
		t = 0;
		while(j <= m && q[j].x > i * siz && q[j].x <= (i + 1) * siz) b[++t] = q[j++];
		sort(&b[1], &b[t + 1], cmp2);
		l = b[1].x, r = b[1].x - 1, tmp = 0;
		memset(v, 0, sizeof v);
		for(int k = 1; k <= t; ++k) {
			if(b[k].x == b[k].y) {
				up[b[k].z] = 0, down[b[k].z] = 1;
				continue;
			}
			if(l < b[k].x) modify(l, b[k].x, -1);
			else if(l > b[k].x) modify(b[k].x, l, 1);
			if(r < b[k].y) modify(r + 1, b[k].y + 1, 1);
			else if(r > b[k].y) modify(b[k].y + 1, r + 1, -1);
			up[b[k].z] = tmp, down[b[k].z] = b[k].y - b[k].x + 1;
			l = b[k].x, r = b[k].y;
		}
	}
	for(int i = 1; i <= m; ++i) {
		if(!down[i] || !up[i]) { puts("0/1"); continue; }
		down[i] = down[i] * (down[i] - 1);
		tmp = gcd(up[i], down[i]);
		printf("%lld/%lld\n", up[i] / tmp, down[i] / tmp);
	}
	return 0;
} 
posted @ 2019-01-22 15:02  water_mi  阅读(202)  评论(0编辑  收藏  举报