【BZOJ】1577: [Usaco2009 Feb]庙会捷运Fair Shuttle

【题意】公车从1开到n,有k群牛想从一个点到达另一个点,公车最多乘坐c个人,牛群可以拆散,问最多载多少牛到达目的地。

【算法】贪心+堆

【题解】线段和点的贪心,一般有按左端点排序和按右端点排序两种方法。

按左端点排序,到达了终点就下车,人数满了就贪心地删掉当前终点最远的牛。

正确性在于,在对左一致的情况下,优先删除对右影响最大的牛。

本来以为很难实现,但是想清楚之后写起来十分顺畅,还是要有信心><

对于到达终点下车,按终点维护小根堆。

对于满人数贪心删终点最大的,维护大根堆。

用标号vis和剩余牛数num连接两个堆的删除。

复杂度O(k log k)。

#include<cstdio>
#include<algorithm>
#include<queue>
using namespace std;
const int maxn=200010;
struct cycmax{
     int id,ed;
     bool operator < (const cycmax &a)const{
         return ed<a.ed;
     }
};
priority_queue<cycmax>cmax;
struct cycmin{
    int id,ed;
    bool operator < (const cycmin &a)const{
        return ed>a.ed;
    }
};
priority_queue<cycmin>cmin;
struct cyc{
    int l,r,num;
}a[maxn];
bool cmp(cyc a,cyc b){return a.l<b.l;}
int k,n,c,ans=0,number=0;
bool vis[maxn];
int main(){
    scanf("%d%d%d",&n,&k,&c);
    for(int i=1;i<=n;i++){
        scanf("%d%d%d",&a[i].l,&a[i].r,&a[i].num);
    }
    sort(a+1,a+n+1,cmp);
    for(int i=1;i<=n;i++){
        cmax.push((cycmax){i,a[i].r});
        cmin.push((cycmin){i,a[i].r});
        number+=a[i].num;
        if(a[i].l!=a[i+1].l){
            while(!cmin.empty()&&cmin.top().ed<=a[i].l){
                cycmin x=cmin.top();
                if(!vis[x.id]){
                    ans+=a[x.id].num;
                    number-=a[x.id].num;
                    vis[x.id]=1;
                }
                cmin.pop();
            }
            while(number>c){
                cycmax x=cmax.top();
                if(!vis[x.id]){
                    if(number-a[x.id].num>=c){
                        number-=a[x.id].num;
                        vis[x.id]=1;
                        cmax.pop();
                    }
                    else{
                        a[x.id].num-=number-c;//zhu yi shun xu le!!!
                        number=c;
                    }
                }else cmax.pop();
            }
        }
    }
    while(!cmax.empty()){
        cycmax x=cmax.top();cmax.pop();
        if(!vis[x.id])ans+=a[x.id].num;
    }
    printf("%d",ans);
    return 0;
}
View Code

另一种做法,按右端点排序,能塞就塞,不能塞就不要,用线段树维护。(感性的理解一下,替换之前的效果一样却反而会占用到后面的,既然前面能解决的事为什么要交给后面的)

posted @ 2017-10-18 21:57  ONION_CYC  阅读(215)  评论(0编辑  收藏  举报