2020牛客多校第八场K题Kabaleo Lite(贪心模拟优先队列 树状数组线段树)
题目链接 https://ac.nowcoder.com/acm/contest/5673/K
题意:输入n个菜品的利润,再输入n个菜品的数量,一个旅客至少要有一个菜品,或者是[1,x]个菜都要,求最大可以接待多少旅客,在这个基础上求最大利润,换句话说,就是求最大旅客数量的最大利润。
题解:首先要求顾客最多,并且只能从第一道菜开始卖,所以最大顾客数就是b[1]。
用c数组记录 1~i 道菜盈利的前缀和,假设 j>i 并且 c[i] < c[j],那肯定是要卖 1~j 更赚钱。假设j<i,并且c[i] < c[j],那肯定也是要卖 1~j 更赚钱。优先队列里用两个pair,c数组为第一关键字,1~i 中b[i]的最小值为第二关键字,原本是第几道菜为第三关键字。用cnt记录已经卖给几个人了,pos记录最小的卖完的菜品下标。在队列里如果队顶的第二关键字>cnt,并且第三关键字 < pos,说明可以更新答案,将第一关键字加到ans中,并更新pos和cnt,直到队列为空。
也就是先卖最高利润的,若最高利润的条件不符合,队列pop;
此题最坑点是爆longlong,需要__int128 该变量只能在Linux系统下使用,本地无法运行的。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
#include<bits/stdc++.h> #define ll __int128 #define pii pair<ll,pair<ll,ll> > using namespace std; const ll maxn=1e5+10; ll a[maxn],b[maxn],c[maxn]; priority_queue<pii> q; inline __int128 read(){ __int128 x=0,f=1; char ch=getchar(); while(ch<'0'||ch>'9'){ if(ch=='-') f=-1; ch=getchar(); } while(ch>='0'&&ch<='9'){ x=x*10+ch-'0'; ch=getchar(); } return x*f; } inline void print(__int128 x){ if(x<0){ putchar('-'); x=-x; } if(x>9) print(x/10); putchar(x%10+'0'); } int main() { ll t; t=read(); int kk=1; while(t--){ ll n,ans=0; n=read(); for(ll i=0;i<n;i++) { a[i]=read(); if(i!=0) c[i]=c[i-1]+a[i]; else c[i]=a[i]; } for(ll i=0;i<n;i++) b[i]=read(); ll bb=b[0]; for(ll i=0;i<n;i++){ bb=min(bb,b[i]); q.push({c[i],{bb,i}}); } ll cnt=0,pos=n; while(!q.empty()){ ll x=q.top().first; ll y=q.top().second.first; ll z=q.top().second.second; q.pop(); if(y<=cnt||pos<=z) continue; ans+=x*(y-cnt); cnt=y; pos=z; } printf("Case #%d: ",kk++); print(b[0]); printf(" "); print(ans); printf("\n"); } return 0; }
题解2:我们用树状数组的差分数组去维护b[i],也可以过。先把a数组求前缀和,按照利润大小排序,贪心最大利润。差分数组维护的是单调不递增的b[i],
比如 b[5,2,3,1,3];
我们维护的差分b为[5,2,2,1,1];
每次卖出一个那么我们就updata(1,-cnt) ,这里不用再updata(pos,+cnt),因为我们用的区间只在前面后面的区间用不到,所有不用更新。
#define IOS ios::sync_with_stdio(false);cin.tie(0);cout.tie(0); #include <bits/stdc++.h> #define inf 0x3f3f3f3f using namespace std; typedef __int128 ll; const int maxn=1e5+7; const ll mod =1e9+7; ll a[maxn],b[maxn],c[maxn],sum[maxn]; int t,n; ll lowbit(ll x){ return x&(-x); } void updata(ll i,ll k){ //在i位置加上k while(i <= n){ c[i] += k; i += lowbit(i); } } ll getsum(ll i){ //求D[1 - i]的和,即A[i]值 int res = 0; while(i > 0){ res += c[i]; i -= lowbit(i); } return res; } void print( ll x ) { if( x<0 ) x=-x,putchar('-'); if( x>9 ) print(x/10); putchar( x%10+'0' ); } void read(ll &x ) { x=0;int f=1;char s=getchar(); for( ;!isdigit(s);s=='-' && (f=-1),s=getchar()); for( ;isdigit(s);x=x*10+s-48,s=getchar()); x*=f; } int main(){ IOS scanf("%d",&t); int kk=1; while(t--){ scanf("%d",&n); vector< pair<ll,ll> >p; for(int i=1;i<=n;i++){ c[i]=0; read(a[i]); sum[i]=sum[i-1]+a[i]; p.push_back( make_pair(sum[i],i)); } sort(p.begin(),p.end()); int mincnt=inf; int lastb=0; for(int i=1;i<=n;i++){ read(b[i]); if(i==1){ mincnt=b[1]; updata(i,b[1]); }else if(b[i]<mincnt) { updata(i,b[i]-mincnt); mincnt=b[i]; }else{ updata(i,0); } } ll ans=0; int len=p.size(); int pos=n; for(int i=len-1;i>=0;i--){ if(p[i].second>pos ) continue; pos=p[i].second; ll cnt=getsum(p[i].second); if( cnt ==0 ) continue; else { ans+=p[i].first*cnt; if(p[i].second==1) break; updata(1,-cnt); //updata(p[i].second+1,cnt); } } printf("Case #%d: ",kk++); print(b[1]); printf(" "); print(ans); printf("\n"); } return 0; }