Educational Codeforces Round 10 D. Nested Segments (一维方法解决二维问题 树状数组)

题目链接

题意

给出一些区间,每个区间用l,r来标识。区间的端点各不相同。求出每个区间所包含其他区间的数量。

思路

参考了官方题解。

官方题解的这种讲述方法很有意义:用一维的方法来解决二维的问题。在这个问题里面,每个区间包含其他的区间,这涉及到各自区间的左端点之间的大小关系、右端点之间的大小关系,这是一个二维的问题。然而我们可以用排序控制左端点的大小关系,进而把它转化为只需要关心右端点的一维问题,这在许多问题中都是非常常见的办法。

具体来说,对于所有点对,我们按照左端点排序,然后以左端点的数值从大到小的顺序进行答案统计。这意味着,考虑当前区间包含的区间数目,只要看在所有已考虑过的右端点中,有多少右端点数值比当前区间的右端点数值小就可以了。这样我们其实就是在动态地维护一个前缀和,每次问询y值以前的前缀和是多少,每次把y值加1。维护这样的前缀和,用到了Fenwick Tree,树状数组。(记一下英文名,下次看题解的时候就不用再查了)。

当然,这里的x,y都是正负1e9的区间,但是最多只有2e5对值,所以我选择把右端点用map离散化。官方题解好像是把右端点排序后取出顺序,其实就是另外一种离散化的方法吧。

代码

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

const int max_n = 2e5 + 10;
map<int, int> mp;
pair<pair<int, int>, int> seg[max_n]; //第一维记录区间,第二维记录编号
vector<int> v;
int n;
int seed = 0;
int comp[max_n];
int ans[max_n];
int toans[max_n];
int ft[max_n]; //树状数组

int lowbit(int x) {
	return x & -x;
}

void add(int pos, int add_num) { 
	//树状数组大小为n,建立在将所有右端点离散化之后的基础上,这样右端点的值为1到n
	for (int i = pos; i <= n; i += lowbit(i)) {
		ft[i] += add_num;
	}
}

int ask(int pos) {
	int ans = 0;
	for (int i = pos; i > 0; i -= lowbit(i)) {
		ans += ft[i];
	}
	return ans;
}

int main() {
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) {
		int x, y;
		scanf("%d %d", &x, &y);
		seg[i] = make_pair(make_pair(x, y), i);
		v.push_back(y);
	}
	//对所有y进行离散化
	sort(v.begin(), v.end());
	for (int i = 0; i < (int)v.size(); i++) {
		mp[v[i]] = ++seed;
	}
	
	//从大到小遍历x,维护y值的前缀和
	sort(seg + 1, seg + n + 1);
	for (int i = n; i > 0; i--) {
		// int x = mp[seg[i].first.first];
		// cout << "x:" << x << endl;
		int y = mp[seg[i].first.second],
			id = seg[i].second;
		// cout << "Y:" << y << endl;
		ans[id] = ask(y);
		add(y, 1);
	}

	for (int i = 1; i <= n; i++) {
		printf("%d\n", ans[i]);
	}

	return 0;
}

总结

当时看到wcn学姐距离上一题只用了20分钟就过了这题,其实就想着这可能是个树状数组,因为学姐给我一种精通树状数组的感觉(强行乱感觉),当然主要原因还是,2e5的数量级,并且她过得这么快,想到相应的数据结构就可能是树状数组。

通过这道题,我对树状数组的理解加深了,更加理解了树状数组作为优秀的单点修改的工具,可以很好地维护前缀和。

并且“一维方法解决二维问题”参考性很强,算是了解了多一种套路。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值