BZOJ 1577: [Usaco2009 Feb]庙会捷运Fair Shuttle 线段树 + 贪心
escription
公交车一共经过N(1<=N<=20000)个站点,从站点1一直驶到站点N。K(1<=K<=50000)群奶牛希望搭乘这辆公交车。第i群牛一共有Mi(1<=Mi<=N)只.
他们希望从Si到Ei去。
公交车只能座C(1<=C<=100)只奶牛。而且不走重复路线,请计算这辆车最多能满足多少奶牛听要求。
注意:对于每一群奶牛,可以部分满足,也可以全部满足,也可以全部不满足。
Input
第1行: 三个整数: K,N,C。 由空格隔开。
第2..K+1行:第i+1行,告诉你第i组奶牛的信息: S_i, E_i and M_i。由空格隔开。
Output
一行:可以在庙会乘坐捷运的牛的最大头数
题解:
只要运输总数最大化就好, 送哪一个牛都是相同的.
优先考虑右端点小的运输段, 右端点相同则以左端点大的优先.
中间过程用线段树模拟一下即可.
这样做为什么是正确的呢 ?
这时依据排序结果可能会导致因为前面的坐满了而后面的上不来的情况.
上一个右端点一定小于等于当前右端点,那么考虑从上一次运输中撤下 $v$ 头牛,加到当前运输中.
当前运输结束的右端点不小于上一个,那么对后面的影响肯定大于之前的那个.
于是,我们就认为如果有右端点更小的,一定要以右端点小的优先.
右端点相同时左端点大的先上车,这样左端点更大在车上时间更少,对之前的影响也更小.
Code:
#include<bits/stdc++.h> #define setIO(s) freopen(s".in","r",stdin) #define maxn 10000000 #define ls (x<<1) #define rs ((x<<1)|1) #define mid ((l+r)>>1) using namespace std; struct Q { int l,r,num; bool operator<(Q e)const { return r==e.r?l>e.l:r<e.r; } }q[maxn]; int k,n,c; int maxv[maxn],lazy[maxn]; void pushup(int x) { maxv[x]=max(maxv[ls],maxv[rs]); } void mark(int x,int delta) { lazy[x]+=delta,maxv[x]+=delta; } void pushdown(int l,int r,int x) { if(lazy[x]) { if(mid>=l) mark(ls, lazy[x]); if(mid+1<=r)mark(rs, lazy[x]); lazy[x]=0; } } int query(int l,int r,int x,int L,int R) { if(l>=L&&r<=R) return maxv[x]; pushdown(l,r,x); int t=0; if(L<=mid) t = query(l,mid,ls,L,R); if(R>mid) t = max(t,query(mid+1,r,rs,L,R)); return t; } void update(int l,int r,int x,int L,int R,int delta) { pushdown(l,r,x); if(l>=L&&r<=R) { mark(x, delta); return; } if(L<=mid) update(l,mid,ls,L,R,delta); if(R>mid) update(mid+1,r,rs,L,R,delta); pushup(x); } int main() { //setIO("input"); scanf("%d%d%d",&k,&n,&c); for(int i=1;i<=k;++i) scanf("%d%d%d",&q[i].l,&q[i].r,&q[i].num); sort(q+1,q+1+k); int ans=0; for(int i=1;i<=k;++i) { int tmp=query(1,n,1,q[i].l,q[i].r-1); ans+=min(q[i].num, c - tmp); update(1,n,1,q[i].l,q[i].r-1,min(q[i].num,c - tmp)); } printf("%d\n",ans); return 0; }