[NOIP 2005 普及组] 校门外的树 (从N≤1000 - N≤1000000000)来自TIGER的人必看!

题目

题目描述

某校大门外长度为 l 的马路上有一排树,每两棵相邻的树之间的间隔都是 1 米。我们可以把马路看成一个数轴,马路的一端在数轴 0 的位置,另一端在 l 的位置;数轴上的每个整数点,即 0,1,2,…,l,都种有一棵树。

由于马路上有一些区域要用来建地铁。这些区域用它们在数轴上的起始点和终止点表示。已知任一区域的起始点和终止点的坐标都是整数,区域之间可能有重合的部分。现在要把这些区域中的树(包括区域端点处的两棵树)移走。你的任务是计算将这些树都移走后,马路上还有多少棵树。

输入格式

第一行有两个整数,分别表示马路的长度 l 和区域的数目 m。

接下来 m 行,每行两个整数 u,v表示一个区域的起始点和终止点的坐标。

输出格式

输出一行一个整数,表示将这些树都移走后,马路上剩余的树木数量

输入样例 

500 3
150 300
100 200
470 471

输出样例

298

题解

1. N≤10000, M≤10000

这个显然很简单,暴力算法就可以过,复杂度O_{(nm)}

那这里也不多说,给个代码:

2. N≤100000, M≤100000

现在,O_{(nm)}过不了了,这时候,我们可以运用到差分算法。

我们知道有个东西叫前缀和,公式是这样的:a_i = a_{i - 1} + s _i

那我们把前缀和反过来,我们去维护 数组a_i = s_i - s_{i - 1},最后,再去用前缀和恢复数组。

而当我们要挖掉一段树的时候,我们的差只在第一个地方和最后一个地方发生改变,所以,要想改变一部分,只需要O_{(1)}的算法,所以整体是O_{(m + n)}的复杂度。

代码如下:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
int main () {
	freopen ("tree.in", "r", stdin);
	freopen ("tree.out", "w", stdout);
	int n, m;
	cin >> n >> m;
	int a [1000010];
	memset (a, 0, sizeof (a));
	while (m --) {
		int i, j;
		cin >> i >> j;
		if (i > j) swap (i, j);
		a [i + 1] ++;
		a [j + 2] --;
	}
	int k [1000010];
	memset (k, 0, sizeof (k));
	int cnt = 0;
	for (int i = 1; i <= n + 1; i ++) {
		k [i] = k [i - 1] + a [i];
		if (k [i] <= 0) cnt ++; 
	} 
	cout << cnt;
	return 0;
}

3. N≤1000000000, M≤100000

此时,上面的方法好似行不通,毕竟空间复杂度太大,不过还是可以拿个40几分的。

这时候,我们可以先以左端点进行排序,并从左向右计算每个端点,若有一个端点的左端点大于这个端点的右端点,则把那个左端点改为这个右端点+1,这样就可以在不开数组的情况下完成了。最后,再加在一起算个总和。

代码如下:

#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
struct tr {
	int x, y;
};
bool cmp (tr a, tr b) {
	if (a.x <= b.x) return true;
	else return false;
}
int main () {
	freopen ("tree2.in", "r", stdin);
	freopen ("tree2.out", "w", stdout);
	int n, m;
	cin >> n >> m;
	tr a [100010];
	memset (a, 0, sizeof (a));
	for (int i = 0; i < m; i ++) {
		cin >> a [i].x >> a [i].y;
		if (a [i].x > a [i].y) swap (a [i].x, a [i].y);
	} 
	sort (a, a + m, cmp); 
	for (int i = 0; i < m - 1; i ++) {
		if (a [i].y < a [i].x) continue;
		int k = 1;
		for (int j = i + 1; j < m; j ++) {
			if (a [i].y > a [j].x) 
			a [j].x = a [i].y + 1;
		}
		
	} 
	int cnt = n + 1;
	for (int i = 0; i < m; i ++) {
		if (a [i].y - a [i].x >= 0) 
		cnt -= a [i].y - a [i].x + 1;
	}
	cout << cnt;
	return 0;
}

  • 5
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值