【2019牛客暑期多校训练营(第七场)】C Governing sand [线段树]

原题传送门

Problem Description

The Wow village is often hit by wind and sand,the sandstorm seriously hindered the economic development of the Wow village.
There is a forest in front of the Wowo village, this forest can prevent the invasion of wind and sand. But there is a rule that the number of tallest trees in the forest should be more than half of all trees, so that it can prevent the invasion of wind and sand. Cutting down a tree need to cost a certain amount of money. Different kinds of trees cost different amounts of money. Wow village is also poor.
There are n kinds of trees. The number of i-th kind of trees is Pi , the height of i-th kind of trees is Hi , the cost of cutting down one i-th kind of trees is Ci.
(Note: “cutting down a tree” means removing the tree from the forest, you can not cut the tree into another height.)

Input

The problem is multiple inputs (no more than 30 groups).
For each test case.
The first line contines one positive integers n (1 ≤ n ≤ 105 ),the kinds of trees.
Then followed n lines with each line three integers Hi (1 ≤ Hi ≤ 109 )-the height of each tree, Ci ( 1 ≤ Ci ≤ 200 )-the cost of cutting down each tree, and Pi ( 1 ≤ Pi ≤ 109 )-the number of the tree.

Output

For each test case, you should output the minimum cost.

Sample Input

2
5 1 1
1 10 1
2
5 1 2
3 2 3

Sample Output

1
2

题目大意

有一片森林,有 n 种树木,第 i 种树的数量是 Pi ,高度是 Hi ,砍掉它需要花费 Ci 的钱。当森林中最高的树木数量超过所有树木数量的一半,才能防止风沙侵袭。不同种类的树木花费不同的钱。Wow村很穷, (好了我要开始编故事了),所以他们需要砍伐树木来换取钱,但是又要保证自己的村落不被风沙侵蚀,那么在卖掉这些树之前, 请输出他们砍伐树木的最低成本。(注意:砍伐是全部砍掉……地表以上没有高度的那种)

思路

从低到高枚举最高的树,在剩下的树当中砍掉代价最小的。使用线段树维护。线段树按照代价从小到大的顺序构建,查询前x个的总和。

样例解释


2
5 1 1
1 10 1

为例: (逃
此时
砍掉一棵5单位高的树,花费1单位的钱,满足其余条件;
砍掉一棵1单位高的树,花费10单位的钱,满足其余条件;
所以当然是前者啦。

Code

本蒟蒻写不出这样的代码,但是本蒟蒻励志看懂她!
(注释是自己对代码的理解……如若有误,很是抱歉,请各位dl指正

/**
 *  I am not responsible of this code.
 *  They made me write it, against my will.
 
 *_______________#########_______________________
 *______________############_____________________
 *______________#############____________________
 *_____________##__###########___________________
 *____________###__######_#####__________________
 *____________###_#######___####_________________
 *___________###__##########_####________________
 *__________####__###########_####_______________
 *________#####___###########__#####_____________
 *_______######___###_########___#####___________
 *_______#####___###___########___######_________
 *______######___###__###########___######_______
 *_____######___####_##############__######______
 *____#######__#####################_#######_____
 *____#######__##############################____
 *___#######__######_#################_#######___
 *___#######__######_######_#########___######___
 *___#######____##__######___######_____######___
 *___#######________######____#####_____#####____
 *____######________#####_____#####_____####_____
 *_____#####________####______#####_____###______
 *______#####______;###________###______#________
 *________##_______####________####______________
 
 **/
 
#include<bits/stdc++.h>
#define O_O ios::sync_with_stdio(false);cin.tie(0);cout.tie(0);
using namespace std;
typedef long long ll;
const int maxn=100010;
 
struct Node{
    int h,c,p,index;                //h->高度 c->代价 p->棵数
    void input(){
        scanf("%d%d%d",&h,&c,&p);
    }
}node[maxn];
 
struct NODE{
    int l,r;                       //左右区间
    ll num,sum;                    //num->棵树 sum->代价
}segtree[maxn*3];
 
bool cmp1(Node n1,Node n2){ return n1.c<n2.c; }
 
bool cmp2(Node n1,Node n2){ return n1.h<n2.h; }
 
void build(int i,int l,int r){                                       
    segtree[i].l=l;
    segtree[i].r=r;
    segtree[i].num=0;
    segtree[i].sum=0;
    if(l==r) return ;
    int mid=l+r>>1;
    build(i<<1,l,mid);
    build(i<<1|1,mid+1,r);
}
 
void push_up(int i){                                                  
    if(segtree[i].l==segtree[i].r) return ;
    segtree[i].num=segtree[i<<1].num+segtree[i<<1|1].num;
    segtree[i].sum=segtree[i<<1].sum+segtree[i<<1|1].sum;
}
 
void add(int i,int index,int c,int p){                               
    if(segtree[i].l==index&&segtree[i].r==index){
        segtree[i].num+=p;
        segtree[i].sum+=(ll)c*p;
        return ;
    }
    int mid=segtree[i].l+segtree[i].r>>1;
    if(index<=mid) add(i<<1,index,c,p);
    else add(i<<1|1,index,c,p);
    push_up(i);
}

ll query(int i,ll num){                                               
    if(num<=0) return 0;
    if(num>=segtree[i].num) return segtree[i].sum;
    if(segtree[i].l==segtree[i].r) return segtree[i].sum/segtree[i].num*num;
    return query(i<<1,num)+query(i<<1|1,num-segtree[i<<1].num);
}
 
int main(){
    int n,t=0;
    while(scanf("%d",&n)!=EOF){
        t++;
        ll ans=0;
        for(int i=0;i<n;i++){
            node[i].input();
            ans+=(ll)node[i].c*node[i].p;   //计算总代价
        }
        sort(node,node+n,cmp1);   //按照砍掉单棵树的代价升序排列
        for(int i=0;i<n;i++) node[i].index=i+1;   //保存代价顺序+1
        build(1,1,n);   //建树
        sort(node,node+n,cmp2);   //按照高度升序排序(为了下面的枚举)
         
        int j=0;
        ll tot=0;   //排序在i之前的树的总和
        ll numoftallest=node[0].p;   //最高树的棵数
        ll res=ans-(ll)node[0].p*node[0].c;   
        //res记录砍掉排序在 当前种类树之后 的所有树的总代价->后缀和
        for(int i=1;i<=n;i++){   //枚举最高高度
            if(i==n||node[i].h!=node[i-1].h){   //若i是最后一个或与前一个高度不同
                ll tmp=query(1,tot-(numoftallest-1))+res;            
                /* 
                    前面还需要砍掉tot-(numoftallest-1)的树
                    因为?
                    因为你看吖
                    numoftallest是不是现在最高的树吖
                    排序在它后面的是不是都被砍掉了吖(res就是后缀和记录排序在它后面的树
                    那所以说numodtallset是不是至少要比tot大1啊
                    那如果此时tot>numoftallest-1
                    是不是有tot-(numoftallest-1)的树要被砍掉啊
                    所以说是不是 要被砍掉的树的代价和+res 啊
                    nba
                 */
                if(ans>tmp) ans=tmp;
                numoftallest=node[i].p;   //i: 不管不管 反正越往后越高 本i最高
                if(i<n){           //如果i不是最后一个
                    while(j<i){             //我终于又见到你了!j  T_T
                        add(1,node[j].index,node[j].c,node[j].p); 
                        tot+=node[j].p;    //tot->排序在当前种类的树前面的树的总和
                        j++;          //j是当前种类树的第一个位置
                    }
                }
            }
            else numoftallest+=node[i].p;        
            /*
                numoftallest->本次枚举的最高树的棵数
               (为什么加了好几次?因为不同种的树也可以高度一样吖)
            */
            if(i<n) res-=(ll) node[i].c*node[i].p;                    
            /*
                相当于另一个写法(非线段树 老板说是贪心)的后缀和 
                你看看你 又不砍了 这样不好 (当然是要全部砍掉啦啊哈哈哈哈吼哈吼吼吼吼…
            */
        }
        printf("%lld\n",ans);
    }
    return 0;
}

事实证明 补题和博客都要好好写 趁自己还记得的时候 QAQ

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

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值