2019牛客暑期多校训练营(第七场)补题

C题:

链接:https://ac.nowcoder.com/acm/contest/887/C

题意: n中树,第i种树有P[i]颗,砍掉每颗树的代价是C[i], 高度是H[i]. 需要用最小的代价砍掉一些树,让最高的树超过一半。

分析:从从低到高枚举最高的树,在剩下的树当中砍掉代价最小的。可以用一颗线段树维护。线段树按照代价从小到大的顺序构建,支持单点插入,查询前x个的总和。

Ac code:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
struct Tree{
    ll num,sum;
}tree[maxn<<2];
struct Node{
    int h,c,p,id;
}node[maxn];
void pushup(int rt)
{
    tree[rt].num=tree[rt<<1].num+tree[rt<<1|1].num;
    tree[rt].sum=tree[rt<<1].sum+tree[rt<<1|1].sum;
}
void buildtree(int rt,int l,int r)
{
    if(l==r){
        tree[rt].sum=tree[rt].num=0;
        return;
    }
    int mid=(l+r)>>1;
    buildtree(rt<<1,l,mid);
    buildtree(rt<<1|1,mid+1,r);
    pushup(rt);
}
ll query(int rt,int l,int r,ll num)///求前num棵树的花费最小总和
{
    if(num<=0) return 0;
    if(num>=tree[rt].num) return tree[rt].sum;
    if(l==r) return tree[rt].sum/tree[rt].num*num;
    int mid=(l+r)>>1;
    return query(rt<<1,l,mid,num)+query(rt<<1|1,mid+1,r,num-tree[rt<<1].num);
}
void update(int rt,int l,int r,int idx,int c,int p)
{
    if(l==r){
        tree[rt].num+=p;
        tree[rt].sum+=1ll*c*p;
        return;
    }
    int mid=(l+r)>>1;
    if(idx<=mid) update(rt<<1,l,mid,idx,c,p);
    else update(rt<<1|1,mid+1,r,idx,c,p);
    pushup(rt);
}
bool cmp1(Node &a,Node &b)
{
    return a.c<b.c;
}
bool cmp2(Node &a,Node &b)
{
    return a.h<b.h;
}
int main()
{
    int n;
    while(~scanf("%d",&n)){
        ll ans=0;
        for(int i=1;i<=n;i++){
            scanf("%d%d%d",&node[i].h,&node[i].c,&node[i].p);
            ans+=1ll*node[i].c*node[i].p;
        }
        sort(node+1,node+n+1,cmp1);
        for(int i=1;i<=n;i++)
            node[i].id=i;
        buildtree(1,1,n);
        sort(node+1,node+n+1,cmp2);
        ll res=ans-1ll*node[1].c*node[1].p;///高于当前最高的树的花费总和
        ll tot=0;///当前加入线段树的树数量总和
        ll maxnum=node[1].p;///当前最高的树的数量总和
        int j=1;
        for(int i=2;i<=n+1;i++)
        {
            if(i==n+1||node[i].h!=node[i-1].h){///每次以i-1为当前最高的树
                ll tmp=query(1,1,n,tot-maxnum+1)+res;///低于当前最高树的前maxnum-1可以留下,所以前tot-(maxnum-1)棵花费最小的树必须砍掉
                ans=min(ans,tmp);
                maxnum=node[i].p;///每次更新给下次用
                if(i<n+1){///i=n+1时更新已经没有意义了
                    while(j<i){
                        update(1,1,n,node[j].id,node[j].c,node[j].p);///按花费从小到大插入线段树
                        tot+=node[j].p;
                        j++;
                    }
                }
            }
            else maxnum+=node[i].p;
            if(i<n+1) res-=1ll*node[i].c*node[i].p;
        }
        printf("%lld\n",ans);
    }
    return 0;
}

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值