[BZOJ2038][2009国家集训队]小Z的袜子(hose) && 莫队算法

用这个玩意就有个要求 就是要能快速用[L, R]的信息得到[L-1, R], [L+1, R]等相邻区间的信息 然后队询问离线 分块处理

把n分成sqrt(n)块 然后按照l把每一个询问放进块里 同一个块里的询问保证r递增 这样每一l移动的长度至多是sqrt(n)*m  r的移动每一块是O(n) 最多移动sqrt(n) * n 则总复杂度为O((n+m) * sqrt(n))

#include<cstdio>
#include<algorithm>
#include<cstring>
#include<iostream>
#include<queue>
#include<cmath>
#define SF scanf
#define PF printf
using namespace std;
typedef long long LL;
const int MAXN = 50000;
struct Node {
	int l, r, id;
	LL a, b;
	bool operator < (const Node &t) const {
		return id < t.id;
	}
} Q[MAXN+10];
int cnt[MAXN+10], A[MAXN+10], block, n, m, L = 1, R;
LL T;
bool cmp(const Node &a, const Node &b) {
	if(a.l / block == b.l / block) return a.r < b.r;
	return a.l < b.l;
}
inline int gcd(LL a, LL b) {
	while(b) {
		LL r = a % b; a = b; b = r;
	}
	return a;
}
void up(int x, int val) {
	T -= cnt[A[x]] * cnt[A[x]];
	cnt[A[x]] += val;
	T += cnt[A[x]] * cnt[A[x]];
}
int main() {
	SF("%d%d", &n, &m);
	block = (int)(sqrt(n) + 1e-7);
	for(int i = 1; i <= n; i++) SF("%d", &A[i]);
	for(int i = 1; i <= m; i++) {
		SF("%d%d", &Q[i].l, &Q[i].r); Q[i].id = i;
	}
	sort(Q+1, Q+1+m, cmp);
	for(int i = 1; i <= m; i++) {
		while(R < Q[i].r) up(++R, 1);
		while(R > Q[i].r) up(R--, -1);
		while(L < Q[i].l) up(L++, -1);
		while(L > Q[i].l) up(--L, 1);
		if(Q[i].l == Q[i].r) {
			Q[i].a = 0; Q[i].b = 1; continue;
		}
		Q[i].a = T - (Q[i].r - Q[i].l + 1);
		Q[i].b = 1LL * (Q[i].r - Q[i].l + 1) * (Q[i].r - Q[i].l);
		LL GCD = gcd(Q[i].a, Q[i].b);
		Q[i].a /= GCD; Q[i].b /= GCD;
	}
	sort(Q+1, Q+1+m);
	for(int i = 1; i <= m; i++) PF("%lld/%lld\n", Q[i].a, Q[i].b);
}


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值