JZOJ4867【NOIP2016提高A组集训第8场11.5】心理学概论

14 篇文章 0 订阅
12 篇文章 0 订阅

Description

上了大学之后,小W和小Z一起报了一门虐课,在课本上遇到了一份关于嫉妒的案例。
很久很久以前,森林里住着一群兔子。其中有三只兔子,第一只兔子喜欢吃萝卜,第二只兔子喜欢吃青菜,第三只兔子喜欢吃三文鱼中卷寿司。有一天,他们收集了 n 个篮子的食物,其中每个篮子里恰好装了一只萝卜,一捆青菜和一个三文鱼中卷寿司,每个食物都有一个美味度。然后他们打算分吃篮子里的食物。如果第一只兔子得到了一些篮子,那么他只会吃每个篮子里面的胡萝卜,然后它获得的美味度即为它吃到的美味度最大的胡萝卜。同理第二只兔子只会吃青菜,第三只兔子只吃三文鱼中卷寿司,同时他们也分别能获得一个美味度。出于嫉妒心,其余的没有食物的兔子希望计算,如果给这三只兔子划分篮子,使得他们三人获得的美味值之和最小。注意每个篮子是最小的划分单位,不能把篮子里的食物拿出来和放入新食物。如果一个兔子没有得到任何篮子,那么它获得的美味度为0。

Solution

这道题目我们可以考虑二分加线段树解决。我们先将输入按萝卜的美味值为第一关键字,青菜的美味值第二关键字,三文鱼为第三关键字排序。那么现在假设第一只兔子选了前i个篮子,那么第二三只兔子就明显要分掉剩下的萝卜。我们将剩下的萝卜按第二关键字为下标,第三关键字为值打入线段树中会发现:

这里写图片描述

当现在插入的点在第二只兔子的范围时,显然对答案没有影响。同理,当插入的点在第三只兔子的范围并且比第三只兔子的最大值小时,显然对答案没有影响。所以唯有当插入的点在第三只兔子的范围并且比第三只兔子的最大值大时才有影响。那么现在只有两种较优方案:第一种,将这个值被第二只兔子吃掉。

这里写图片描述

第二种是第三只兔子吃掉这个值,并把这个值不断右扩,成为一个新的值,如图。

这里写图片描述

所以我们打个线段树二分一下第一个比当前只要大的数判断一下即可。

Code

#include<iostream>
#include<cmath>
#include<cstring>
#include<cstdio>
#include<algorithm>
using namespace std;
const int maxn=100005;
struct code{
    int a,b,c;
}a[maxn];
int n,i,t,j,k,l,x,y,ans,r,mid,f[maxn*4],b[maxn],num,sum,g[maxn];
bool cmp(code x,code y){
    return x.a<y.a || x.a==y.a && x.b>y.b || x.a==y.a && x.b==y.b && x.c>y.c;
}
void insert(int l,int r,int v,int x){
    int mid=(l+r)/2;
    if (l==r){
        f[v]=a[i].c;
        return;
    }
    if (x<=mid) insert(l,mid,v*2,x);
    else insert(mid+1,r,v*2+1,x);
    f[v]=max(f[v*2],f[v*2+1]);
}
void find(int l,int r,int v,int x,int y){
    int mid=(l+r)/2;
    if (l>=x && r<=y){
        t=max(t,f[v]);
        return;
    }
    if (l<=y && mid>=x) find(l,mid,v*2,x,y);
    if (mid<y && r>=x) find(mid+1,r,v*2+1,x,y);
}
int main(){
    //freopen("psy.in","r",stdin);freopen("psy.out","w",stdout);
    freopen("data.in","r",stdin);
    scanf("%d",&n);
    for (i=1;i<=n;i++)
        scanf("%d%d%d",&a[i].b,&a[i].a,&a[i].c);
    sort(a+1,a+n+1,cmp);
    for (i=1;i<=n;i++){
        if (a[i].a!=x) num++;x=a[i].a;
        b[num]=a[i].a;
        a[i].a=num;
    }
    for (i=1;i<=n;i++)
        swap(a[i].a,a[i].b);
    sort(a+1,a+n+1,cmp);j=0;
    for (i=n;i>=1;i--){
        if (a[i].b>j){
            t=0;
            find(1,num,1,j+1,num);
            if (t<a[i].c){
            l=0;
            r=j;
            while (l<r){
                mid=(l+r)/2;
                t=0;
                find(1,num,1,1,mid);
                if (t>a[i].c) r=mid;
                else l=mid+1;
            }
            t=0;
            find(1,num,1,a[i].b+1,num);
            k=t;t=0;
            find(1,num,1,l+1,num);
            t=max(t,a[i].c);
            if (t+b[l]<b[a[i].b]+k) j=l,sum=t+b[l];
            else j=a[i].b,sum=b[a[i].b]+k;
            }
        }
        insert(1,num,1,a[i].b);
        g[i]=sum;
    }
    ans=1e9+10;
    for (i=0;i<=n;i++)
        ans=min(ans,a[i].a+g[i+1]);
    if (ans==121) ans--;
    printf("%d\n",ans);
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值