P1607 [USACO09FEB]庙会班车Fair Shuttle(贪心+线段树维护)

题目描述
Although Farmer John has no problems walking around the fair to collect prizes or see the shows, his cows are not in such good shape; a full day of walking around the fair leaves them exhausted. To help them enjoy the fair, FJ has arranged for a shuttle truck to take the cows from place to place in the fairgrounds.

FJ couldn’t afford a really great shuttle, so the shuttle he rented traverses its route only once (!) and makes N (1 <= N <= 20,000) stops (conveniently numbered 1…N) along its path. A total of K (1 <= K <= 50,000) groups of cows conveniently numbered 1…K wish to use the shuttle, each of the M_i (1 <= M_i <= N) cows in group i wanting to ride from one stop S_i (1 <= S_i < E_i) to another stop E_i (S_i < E_i <= N) farther along the route.

The shuttle might not be able to pick up an entire group of cows (since it has limited capacity) but can pick up partial groups as appropriate.

Given the capacity C (1 <= C <= 100) of the shuttle truck and the descriptions of the groups of cows that want to visit various sites at the fair, determine the maximum number of cows that can ride the shuttle during the fair.

逛逛集市,兑兑奖品,看看节目对农夫约翰来说不算什么,可是他的奶牛们非常缺乏锻炼——如果要逛完一整天的集市,他们一定会筋疲力尽的。所以为了让奶牛们也能愉快地逛集市,约翰准备让奶牛们在集市上以车代步。但是,约翰木有钱,他租来的班车只能在集市上沿直线跑一次,而且只能停靠N(1 ≤N≤20000)个地点(所有地点都以1到N之间的一个数字来表示)。现在奶牛们分成K(1≤K≤50000)个小组,第i 组有Mi(1 ≤Mi≤N)头奶牛,他们希望从Si跑到Ti(1 ≤Si<Ti≤N)。

由于班车容量有限,可能载不下所有想乘车的奶牛们,此时也允许小里的一部分奶牛分开乘坐班车。约翰经过调查得知班车的容量是C(1≤C≤100),请你帮助约翰计划一个尽可能满足更多奶牛愿望的方案。

输入格式
【输入】

第一行:包括三个整数:K,N和C,彼此用空格隔开。

第二行到K+1行:在第i+1行,将会告诉你第i组奶牛的信息:Si,Ei和Mi,彼

此用空格隔开。

输出格式
【输出】

第一行:可以坐班车的奶牛的最大头数。
输入 #1
8 15 3
1 5 2
13 14 1
5 8 3
8 14 2
14 15 1
9 12 1
12 15 2
4 6 1
输出 #1
10
【样例说明】

班车可以把2头奶牛从1送到5,3头奶牛从5送到8,2头奶牛从8送到14,1头

奶牛从9送到12,1头奶牛从13送到14,1头奶牛从14送到15。

题意: 题意同会场安排问题很像,k组牛,每组牛都有想去的[ l , r ]区间,但不同的是车辆有载牛上限,所以需要知道某个区间内的牛数量的最大值,用线段树来维护。注意每组牛可以只去一部分牛。
思路:

  1. 同会场安排的贪心方式,每组按右端点由小到大排序,右端点你相同则按左端点由小到大排序。
  2. 用线段树维护区间最大值;注意维护时,将某组牛放到cnt车上时,不是区间的加,而是类似于区间加(具体看注释),查到最大值res后,就只能再放入C-res个牛;
  3. 排序后遍历一波,记录即可。

ac代码:

#include<bits/stdc++.h>
#define ll long long
using namespace std;
const int maxn=1e5+7;
const ll INF=1e9;
struct node
{
    ll l,r,v;
}stu[maxn];
ll sum[maxn*4],add[maxn*4];
bool cmp(node a,node b)
{
    if(a.r==b.r)    return a.l<b.l;
    return a.r<b.r;
}
void pushdown(ll root,ll k)
{
    if(add[root])
    {
        sum[root<<1]+=add[root];                ///不是区间求和操作,区间求和应为sum[root<<1]+=(1-k>>1)*add[root],下行也需改
        sum[root<<1|1]+=add[root];
        add[root<<1]+=add[root];
        add[root<<1|1]+=add[root];
        add[root]=0;
    }
}
void change(ll root,ll l,ll r,ll ql,ll qr,ll k)
{
    if(ql<=l&&qr>=r)
    {
        sum[root]+=k;                   ///区间求和为sum[root]+=(r-l)*k;这样查询时就能保证查到的是当前区间的最大值,否则查到的值会偏大。
        add[root]+=k;
        return;
    }
    ll mid=(l+r)>>1;
    pushdown(root,r-l+1);
    if(ql<=mid) change(root<<1,l,mid,ql,qr,k);
    if(qr>mid)  change(root<<1|1,mid+1,r,ql,qr,k);
    sum[root]=max(sum[root<<1],sum[root<<1|1]);
}
ll q(ll root,ll l,ll r,ll ql,ll qr)
{
    if(ql<=l&&qr>=r)
    {
        return sum[root];
    }
    ll mid=(l+r)>>1;
    pushdown(root,r-l+1);
    ll ans=0;
    if(ql<=mid)       ans=max(ans,q(root<<1,l,mid,ql,qr));
    if(qr>mid)        ans=max(ans,q(root<<1|1,mid+1,r,ql,qr));
    return ans;
}
int main()
{
    ll n,m,k;
    scanf("%lld%lld%lld",&n,&m,&k);
    for(int i=1;i<=n;i++)
        scanf("%lld%lld%lld",&stu[i].l,&stu[i].r,&stu[i].v);
    sort(stu+1,stu+1+n,cmp);
    ll ans=0;
    for(int i=1;i<=n;i++)
    {
        stu[i].r--;			//这里因为在都端点当前牛下车的同时,其他牛也可以上车,所以右端点减一。
        ll res=q(1,1,m,stu[i].l,stu[i].r);	
        if(res<k)
        {
            ll cnt=min(k-res,stu[i].v);				
            change(1,1,m,stu[i].l,stu[i].r,cnt);
            ans+=cnt;
            //printf("l: %lld r: %lld cnt: %lld\n",stu[i].l,stu[i].r,cnt);
        }
    }
    printf("%lld\n",ans);
}

以下是P4087 [USACO17DEC]Milk Measurement的c++代码: ```c++ #include<bits/stdc++.h> using namespace std; int n,d,i,x,minn=1e9,maxn=-1e9,sum=7;//注意sum要初始化为7,因为一开始有三个人挤奶! map<int,int> mp; struct node{ int day,milk,id;//day表示某一天,milk表示这一天的产奶量,id表示这头牛的编号 }a[100010]; bool cmp(node x,node y){ return x.day<y.day; } int main(){ scanf("%d%d",&n,&d); for(i=1;i<=n;i++){ scanf("%d%d%d",&a[i].day,&a[i].id,&a[i].milk); minn=min(minn,a[i].id);//记录最小的牛的编号 maxn=max(maxn,a[i].id);//记录最大的牛的编号 } sort(a+1,a+n+1,cmp);//排序 for(i=1;i<=n;i++){ int p=a[i].id; mp[p]+=a[i].milk;//记录每头牛产奶总量 if(mp[p]-a[i].milk>=mp[minn]&&mp[p]>=mp[minn]){//如果这头牛的产奶总量减去这一天的产奶量后等于最小产奶量且这头牛的产奶总量大于等于最小产奶量 sum--; } if(mp[p]>=mp[maxn]&&mp[p]-a[i].milk<mp[maxn]){//如果这头牛的产奶总量大于等于最大产奶量且这头牛的产奶总量减去这一天的产奶量小于最大产奶量 sum++; } if(mp[p]-a[i].milk<mp[maxn]&&mp[p]>=mp[maxn]){//如果这头牛的产奶总量减去这一天的产奶量小于最大产奶量且这头牛的产奶总量大于等于最大产奶量 if(mp[maxn]-mp[p]+a[i].milk>0)sum++; } mp[p]-=a[i].milk;//减去这一天的产奶量 if(i==n||a[i].day!=a[i+1].day){//如果到了新的一天或者到了最后一天 if(mp[maxn]!=mp[a[i].id]&&mp[a[i].id]>=mp[maxn])sum++;//如果这头牛的产奶总量不等于最大产奶量且这头牛的产奶总量大于等于最大产奶量 if(mp[maxn]==mp[a[i].id]){//如果这头牛的产奶总量等于最大产奶量 if(a[i].id==maxn)sum+=0;//如果这头牛就是最大产奶量的牛,那么不需要增加计数器 else sum++;//否则需要增加计数器 } if(mp[minn]!=mp[a[i].id]&&mp[a[i].id]>=mp[minn])sum++;//如果这头牛的产奶总量不等于最小产奶量且这头牛的产奶总量大于等于最小产奶量 if(mp[minn]==mp[a[i].id]){ if(a[i].id==minn)sum+=0;//如果这头牛就是最小产奶量的牛,那么不需要增加计数器 else sum++;//否则需要增加计数器 } } } printf("%d\n",sum); return 0; } ``` 该题的解题思路是模拟,需要注意细节问题。我们可以首先将输入的数据按天数排序,然后模拟每一天挤奶的情况,并根据题目要求进行计数即可。具体细节请见代码注释。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值