题意:
洛谷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;
}