51nod1290 Counting Diff Pairs

题面

在这里插入图片描述
题目连接

解题思路

使用莫队+树状数组的做法是显然的,但是复杂度是 O ( n n l o g n ) O(n \sqrt{nlogn}) O(nnlogn )
介绍一种基于二次离线的做法,复杂度 O ( n n ) O(n\sqrt n) O(nn )
在莫队区间移动时,例如从 [ l , r ] [l,r] [l,r]变成 [ l , r + 1 ] [l,r+1] [l,r+1]时,我们需要求得 [ l , r ] [l,r] [l,r]中有对少个数和r+1有关。这可以拆分成对于 [ 1 , l − 1 ] [1,l-1] [1,l1] [ 1 , r ] [1,r] [1,r]的两个前缀的询问。由于前缀只有 n n n个,我们采用 O ( n ) O(\sqrt n) O(n )修改, O ( 1 ) O(1) O(1)查询的数据结构,分块即可。
因此我们可以将莫队区间每次移动的复杂度优化到了 O ( 1 ) O(1) O(1),整体 O ( n n ) O(n\sqrt n) O(nn )

代码

#include <bits/stdc++.h>
using namespace std;

typedef long long ll;

const int N = 5e4 + 100;
const int SZ = 223;

int aa[N], bb[N];

void add(int id, int x) {
	while (id % SZ != 0) aa[id++] += x;
	while (id < N) bb[id / SZ] += x, id += SZ;
}

int query(int id) {
	return aa[id] + bb[id / SZ];
}

struct node{
	int l, r, id;
}qu[N];

bool operator < (node a, node b) {
	if (a.l / SZ == b.l / SZ) return a.r < b.r;
	return a.l < b.l;
}

struct que{
	int l, r, op, id;
	que() {}
	que(int l, int r, int op, int id):l(l), r(r), op(op), id(id) {}
};

int n, k, m, tp;
int sa[N], has[N], ql[N], qr[N];
ll s1[N], s2[N], ans[N];
vector<que> V[N];

int get(int id) {
	return query(qr[id]) - query(ql[id] - 1);
}

int main() {
//	freopen("input.txt", "r", stdin);
	scanf("%d%d%d", &n, &k, &m);
	for (int i = 1; i <= n; i++) scanf("%d", sa + i), has[i] = sa[i];
	for (int i = 1; i <= m; i++) scanf("%d%d", &qu[i].l, &qu[i].r), qu[i].l++, qu[i].r++, qu[i].id = i;
	sort(has + 1, has + n + 1);
	tp = unique(has + 1, has + n + 1) - has - 1;
	for (int i = 1; i <= n; i++) {
		ql[i] = lower_bound(has + 1, has + tp + 1, sa[i] - k) - has;
		qr[i] = upper_bound(has + 1, has + tp + 1, sa[i] + k) - has - 1;
		sa[i] = lower_bound(has + 1, has + tp  + 1, sa[i]) - has;
	}
	sort(qu + 1, qu + m + 1);
	int l = 1, r = 0;
	for (int i = 1; i <= m; i++) {
		if (r < qu[i].r) {
			V[l - 1].push_back(que(r + 1, qu[i].r, -1, qu[i].id));
			r = qu[i].r;
		}
		if (l > qu[i].l) {
			V[r].push_back(que(qu[i].l, l - 1, 1, qu[i].id));
			l = qu[i].l;
		}
		if (r > qu[i].r) {
			V[l - 1].push_back(que(qu[i].r + 1, r, 1, qu[i].id));
			r = qu[i].r;
		}
		if (l < qu[i].l) {
			V[r].push_back(que(l, qu[i].l - 1, -1, qu[i].id));
			l = qu[i].l;
		}
	}
	for (int i = 1; i <= n; i++) {
		add(sa[i], 1);
		s1[i] = s1[i - 1] + get(i);
		if (i + 1 <= n) s2[i] = s2[i - 1] + get(i + 1);
		for (que t : V[i]) {
			for (int i = t.l; i <= t.r; i++) ans[t.id] += t.op * get(i);
		}
	}
	l = 1, r = 0; ll res = 0;
	for (int i = 1; i <= m; i++) {
		res += ans[qu[i].id];
		if (r < qu[i].r) {
			res += s2[qu[i].r - 1] - s2[r - 1]; 
			r = qu[i].r;
		}
		if (l > qu[i].l) {
			res -= s1[l - 1] - s1[qu[i].l - 1];
			l = qu[i].l;
		}
		if (r > qu[i].r) {
			res -= s2[r - 1] - s2[qu[i].r - 1];
			r = qu[i].r;
		}
		if (l < qu[i].l) {
			res += s1[qu[i].l - 1] - s1[l - 1];
			l = qu[i].l;
		}
		ans[qu[i].id] = res; 
	}
	for (int i = 1; i <= m; i++) printf("%lld\n", ans[i]);
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值