洛谷P1083 借教室 题解

题目

[NOIP2012 提高组] 借教室

题解

这道题是几周之前做到的一道题,本来不想讲的,因为这道题也是用到了二分答案的方法,这类题目之前已经发布过两篇题解了。但这道题还运用了差分数组这个思想,所以我觉得还是值得讲一讲的。

首先,什么是差分数组呢?就是对于某一个数组,每两个相邻元素的差值组成的新的数组。比如对于数组 \(a[4]=[1,4,2,6]\) ,其差分数组即为 \([0,3,-2,4]\) (首元素设为0方便解题)。

所以回到这道题,我们该如何获取每天所需要的教室呢?暴力求解最坏复杂度高达 \(O(mn)\) ,这显然是不行的,所以要用到差分数组的思想。对于原数组每个区间 \([s,t]\) 的所有元素都增加 \(d\) ,差分数组 \(diff\) 是如何变化呢?很简单

\[diff[s]+=d, diff[t+1]-=d \]

想一想是不是,除了这两个元素其他值都是保持不变的。求出 \(diff\) 数组后求出原数组就很容易的,原数组 \(need\) 的表达式即为

\[need[i]=need[i-1]+diff[i] \]

复杂度只有 \(O(m+n)\) 。然后再将每天的需求量与现有量比较,如果都满足,则直接输出 \(0\) ,否则运用二分答案,同样运用差分数组的方法找到第一个无法满足的订单,即为所需答案。程序总的复杂度为 \(O((m+n)\cdot logm)\)

代码

#include<iostream>
#include<cstring>
using namespace std;
int* rest, * need;
int diff[1000002];
int *d, *s, *t;
int n, m;

bool ifcan(int x)
{
	memset(diff, 0, sizeof(diff));
	for (int i = 0; i < x; i++)
	{
		diff[t[i] + 1] -= d[i];
		diff[s[i]] += d[i];
	}
	for (int i = 1; i <= n; i++)
	{
		need[i] = need[i - 1] + diff[i];
		if (need[i] > rest[i])
			return 0;
	}
	return 1;
}

int main()
{
	cin >> n >> m;
	rest = new int[n + 1],need = new int[n + 1];
	d = new int[m], s = new int[m], t = new int[m];
	need[0] = 0;
	for (int i = 1; i <= n; i++)
		scanf("%d", &rest[i]);
	for (int i = 0; i < m; i++)
	{
		scanf("%d%d%d", &d[i], &s[i], &t[i]);
	}
	if (!ifcan(m))
	{
		cout << -1 << endl;
		int l = 1, r = m;
		int mid;
		while (l < r)
		{
			mid = (l + r) / 2;
			if (ifcan(mid))
				l = mid + 1;
			else
				r = mid;
		}
		cout << r;
	}
	else
		cout << 0;
	return 0;
}
posted @ 2021-08-24 11:29  反演的莫比乌斯环  阅读(231)  评论(0)    收藏  举报