[NOIpTG2012]借教室——[二分+差分]

[NOIpTG2012]借教室——[二分+差分]_第1张图片
[NOIpTG2012]借教室——[二分+差分]_第2张图片
【题意分析】

借此题复习差分与二分

首先答案是有可二分性的——到第i个人可以,那么到i前面的人全都是可行的

那么我们先二分,但是每次怎么统计能不能满足到第mid个人的任务要求呢?

注意到问题转化为每天的供应量能不能满足需求量,乍一看好像是线段树题

其实没有这么麻烦,考虑差分:对于一个询问: [ l i , r i ] [l_i,r_i] [li,ri]需求量为 d i d_i di本来是区间加,但是我们只要在 l i l_i li处加 d i d_i di,在 r i + 1 r_i+1 ri+1处减 d i d_i di,然后我们统计差分数组的前缀和 p r e i pre_i prei就是 i i i处的需求量

然后再 O ( n ) O(n) O(n)扫一遍即可

Code:

#include 
#include 
#include 
#include 
#include 
#include 
#define int long long
#define MAXN 1000010
using namespace std;

int delta[MAXN], l[MAXN], r[MAXN], d[MAXN], a[MAXN], n, m, ans = -1;

inline int read () {
	register int s = 0, w = 1;
	register char ch = getchar ();
	while (! isdigit (ch)) {if (ch == '-') w = -1; ch = getchar ();}
	while (isdigit (ch)) {s = (s << 3) + (s << 1) + (ch ^ 48); ch = getchar ();}
	return s * w;
}

inline bool check (int x) {
	memset (delta, 0, sizeof delta); int pre = 0;
	for (register int i = 1; i <= x; i++)
		delta[l[i]] += d[i], delta[r[i] + 1] -= d[i];
	for (register int i = 1; i <= n; i++) {
		pre += delta[i];
		if (pre > a[i]) return 0;
	}
	return 1;
}

signed main () {
	n = read (), m = read ();
	for (register int i = 1; i <= n; i++) a[i] = read ();
	for (register int i = 1; i <= m; i++) d[i] = read (), l[i] = read (), r[i] = read ();
	int L = 1, R = m;
	while (L <= R) {
		int mid = L + R >> 1;
		if (check (mid)) L = mid + 1; else ans = mid, R = mid - 1;
	}
	if (ans == -1) puts ("0"); else printf ("-1\n%lld\n", ans);
	return 0;
}

你可能感兴趣的:(二分,差分)