洛谷P1937 [USACO10MAR]Barn Allocation G

题意:
洛谷P1937 [USACO10MAR]Barn Allocation G

题意:

(N)个编号为(1-N)的点,每个点有一个权值(a[i])。给你(M)个指令,每个指令包含两个数(l,r)表示把区间([l,r])的每个点的权值减一。要求每个点的权值不能为负数,求最多能满足几个指令。

题解:

这道题是一道很经典的题"活动安排"的变式,做法和它也很像。

先考虑把所有的指令以右端点为第一关键字从小到大排序,右端点相同则按左端点从小到大排序。遍历排序后的指令,若能满足(即区间([l,r])的最小值大于零)就满足他,否则就跳过。

区间修改和区间最值,用线段树就可以了。

正确性证明:

如果线段不矛盾都选上就好了,所以考虑矛盾的线段,如图:

考虑线段([l1,r1])([l2,r2])矛盾,也就是(min([l2,r2])=1)

此时,如果我们选择区间([l2,r2]),并且假设这种方法比([l1,r1])更优,那么多出来的线段一定在区间([l1,l2])中。由于我们是按照区间右端点排序的,所以在([l1,l2])中一定没有别的线段。反观区间([r1,r2])由于选择了第二个区间,所以([r1,r2])与之前相比是多减了1的,一定不比选择([l1,r1])优。

再考虑当两个区间右端点相同,即([l1,r1],[l2,r1]),那么此时不论怎么选,都不会对下一条线段产生影响。

代码:

#include<bits/stdc++.h>
using namespace std;
const int N = 100005;
int n, m, a[N], ans;
int T[N << 2], minn[N << 2], lazy[N << 2];
struct node
{
	int l, r;
}nod[N];
bool cmp(node a, node b)
{
	if(a.r != b.r) return a.r < b.r;
	else return a.l < b.l;
}
void pushup(int cnt)
{
	minn[cnt] = min(minn[cnt << 1], minn[cnt << 1 | 1]);
}
void build(int cnt, int l, int r)
{
	if(l == r)
	{
		minn[cnt] = a[l];
		return;
	}
	int mid = l + r >> 1;
	build(cnt << 1, l, mid);
	build(cnt << 1 | 1, mid + 1, r);
	pushup(cnt);
}
void pushdown(int cnt, int l, int r)
{
	if(lazy[cnt])
	{
		minn[cnt << 1] += lazy[cnt];
		minn[cnt << 1 | 1] += lazy[cnt];
		lazy[cnt << 1] += lazy[cnt];
		lazy[cnt << 1 | 1] += lazy[cnt];

		lazy[cnt] = 0;
	}
}
void update(int cnt, int l, int r, int nl, int nr)
{
	if(l >= nl && r <= nr)
	{
		lazy[cnt] -= 1;
		minn[cnt] -= 1;
		return;
	}
	int mid = l + r >> 1;
	pushdown(cnt, l, r);
	if(mid >= nl) update(cnt << 1, l, mid, nl, nr);
	if(mid < nr) update(cnt << 1 | 1, mid + 1, r, nl, nr);
	pushup(cnt);
}
int qmin(int cnt, int l, int r, int nl, int nr)
{
	if(l >= nl && r <= nr) return minn[cnt];
	int mid = l + r >> 1;
	pushdown(cnt, l, r);
	int ans = 999999999;
	if(mid >= nl) ans = min(ans, qmin(cnt << 1, l, mid, nl, nr));
	if(mid < nr) ans = min(ans, qmin(cnt << 1 | 1, mid + 1, r, nl, nr));
	return ans;
}
int main()
{
	scanf("%d%d", &n, &m);
	for(int i = 1; i <= n; i ++) scanf("%d", &a[i]);
	build(1, 1, n);
	for(int i = 1; i <= m; i ++) scanf("%d%d", &nod[i].l, &nod[i].r);
	sort(nod + 1, nod + 1 + m, cmp);
	for(int i = 1; i <= m; i ++)
	{
		if(qmin(1, 1, n, nod[i].l, nod[i].r) > 0)
		{
			update(1, 1, n, nod[i].l, nod[i].r);
			ans ++;
		}
	}
	cout << ans;
}
原文地址:https://www.cnblogs.com/lcezych/p/12995048.html