洛谷刷题——P1047 [NOIP2005 普及组] 校门外的树

题目:来源于洛谷
在这里插入图片描述
分析:
看了题目之后,要求输出剩下的数。而树分为砍和不砍,可以使用bitset,那什么是bitset呢?

bitset是一串二进制码(只含0和1,长度自己设),当然使用bitset需要包含头文件 bitset 。

定义方式:bitset < n > name,n是长度,name是名字;定义好之后,默认里面存的都是0

话不多说,上代码:

#include <iostream>
#include <cstdio>
#include <bitset>

using namespace std;

bitset<1005> bt;

int main()
{

	//输入

	int len, m;
	cin >> len >> m;

	//外层循环,控制输入几组数据
	
	for (int i = 0; i < m; ++i) {
		
		int start, end;			//开始区间 和 结束区间
		cin >> start >> end;

		//内层循环,把bt里面的值改为1,也就是把树砍了
		for (; start <= end; ++start) {
			bt[start] = 1;
		}

	}

	//统计没有砍的树

	int count = 0;

	for (int i = 0; i <= len; ++i) {
		if (!bt[i]) {
			count++;
		}
	}

	cout << count << endl;

	return 0;
}

继续刷题ing (>人<;)

<think>好的,我现在需要解决用户关于NOIP 2005普及组目P1047校门外”的解思路和代码实现的问。首先,我应该回忆一下目的大致内容。目是说,有一条马路上有若干棵,给出一些区域需要移走这些区域的,最后求剩下多少棵。这听起来像是一个区间覆盖的问。 用户提供的引用中有提到,解思路是用数组表示所有,初始设为1,移走的设为0,最后统计1的数量。这个方法应该可行。那具体怎么做呢?比如,马路长度可能到10000,所以数组大小应该足够,初始化为1。然后处理每个区间,把区间内的元素置为0。最后遍历数组统计1的数量。 首先,我需要确认目中的输入格式。通常这类目第一行是两个整数L和M,L代表马路的长度(即的数量是L+1,因为包括0和L点),M是区域的数量。接下来M行每行两个整数,表示区域的起始点和结束点。例如,输入可能是: 500 3 150 300 100 200 470 471 这时需要处理这三个区间,将对应的位置设为0。需要注意的是,区间可能重叠,但用数组方法的话,重复设置0也不影响结果,所以不需要特殊处理重叠。 接下来,我需要考虑如何高效地处理这些区间。例如,对于每个区间,循环从起始到结束的位置,将数组元素置0。这在L较大的情况下会不会很慢?比如L是10000的话,这样的操作时间复杂度是O(M*L),假设M是100的话,总共有1e6次操作,这在现代计算机上应该没问,尤其是对于编程竞赛来说,这样的时间复杂度是可以接受的。 然后,代码实现方面,可以用一个数组,初始化所有元素为1。然后读取每个区间,循环设置对应位置为0。最后统计数组中1的个数。 比如Python代码的话,可以这样写: L, M = map(int, input().split()) trees = [1] * (L + 1) for _ in range(M): start, end = map(int, input().split()) for i in range(start, end + 1): trees[i] = 0 print(sum(trees)) 不过,当L很大时,比如L=1e5,这样的方法可能会比较慢,因为每次都要循环区间内的每个点。有没有优化的方法?比如用差分数组,或者区间合并的方法? 不过原中L的范围是多少呢?根据NOIP普及组目,通常数据规模不会太大。比如原中的L可能到10000,M到100。这样的话,上述方法完全没问。所以用户提供的引用中的思路是正确的,直接按照数组处理即可。 可能需要注意的边界条件是,区间的端点是否包含。例如,目中的区间是闭区间,所以start到end都要包括,所以在循环时要到end+1吗?或者是在Python中的range是左闭右开的,所以应该是range(start, end+1),因为i从start到end(包括end),所以循环到end+1的前一位。例如,当start=100,end=200时,循环i从100到200,即range(100, 201)? 是的,这样是正确的。所以在代码中,将i从start到end(包括end),所以在Python中用range(start, end+1)。 那这个代码应该可以解决问。但有没有其他方法?比如,如果L很大,比如1e8,这时候数组会超出内存限制,此时就需要用区间合并的方法。但对于本来说,可能不需要,因为目给出的L应该是足够小的。 比如,在引用中的第二个引用提到“这个的思路是先把所有的都用数组表示出来,赋值为1,移走的用0表示,最后统计剩下的1的数量”,所以这应该就是正确的解法。用户给出的示例代码是否正确? 比如,用户提供的引用中的示例可能是一个类似的问,比如P5728,但这里的P1047应该用同样的思路。因此,我认为用户提供的思路是正确的,代码应该按照这个思路来写。 总结一下,解步骤: 1. 初始化数组表示每棵的状态,初始为1(存在)。 2. 遍历每个区间,将区间内的的状态设为0(移走)。 3. 统计数组中1的数量,即剩余的的数量。 需要注意的点是,数组的长度是L+1,因为包括0到L的位置。例如,当L=500时,有501棵。 现在,我需要将这个过程用Python代码写出来,并确保正确性。例如,测试样例: 输入: 500 3 150 300 100 200 470 471 处理这三个区间后,剩下的有多少? 第一个区间覆盖150到300,第二个覆盖100到200,第三个覆盖470到471。合并后的覆盖范围是从100到300,以及470到471。总共有(300-100+1)=201棵,加上2棵,总共移走203棵。原本有501棵,所以剩下的501-203=298棵? 但用数组的方法的话,代码会正确计算重叠区域吗?是的,因为即使重复置0,也不会影响最终结果。 所以这段代码是正确的。 因此,用户的问需要这样的解答,给出解思路和示例代码,同时生成相关问。</think>### NOIP 2005 普及组 P1047 校门外思路与代码实现 #### 目分析 目要求在一条长度为 $L$ 的马路上(包含 $0$ 到 $L$ 的整点)移除若干区间内的,最终统计剩余的数量。通过数组标记法可以高效解决此问。 #### 解步骤 1. **初始化数组**:创建长度为 $L+1$ 的数组 `trees`,所有元素初始化为 `1`,表示所有位置均有。 2. **处理区间**:对于每个输入的区间 $[u, v]$,将数组中下标为 $u$ 到 $v$ 的元素置为 `0`,表示移除该区间的。 3. **统计剩余的数量**:遍历数组,统计 `1` 的数量。 #### 示例代码 ```python L, M = map(int, input().split()) trees = [1] * (L + 1) for _ in range(M): start, end = map(int, input().split()) for i in range(start, end + 1): trees[i] = 0 print(sum(trees)) ``` #### 代码说明 - **输入处理**:首行输入 $L$ 和 $M$,分别表示马路长度和区间数量。 - **数组初始化**:`trees` 数组的长度为 $L+1$,对应 $0$ 到 $L$ 的所有位置。 - **区间处理**:通过双重循环标记移除的,时间复杂度为 $O(M \cdot \text{平均区间长度})$,适用于目给定的数据范围。 - **结果输出**:直接对数组求和,统计剩余的。 #### 优化思考 若 $L$ 极大(如 $10^8$),数组会超出内存限制。此时可通过**区间合并**优化: 1. 将所有区间按起点排序并合并重叠部分。 2. 计算合并后各区间的总覆盖长度,最终剩余数为 $L+1 - \text{总覆盖长度}$[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值