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系统下使用,本地无法运行的。

#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;
}
View Code

题解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;
}

 

posted @ 2020-08-03 23:14  杰瑞与汤姆  阅读(153)  评论(0编辑  收藏  举报