Governing sand(主席树/贪心)(2019牛客暑期多校训练营(第七场))

示例:
输入:
2
5 1 1
1 10 1
2
5 1 2
3 2 3
输出:
1
2

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

题解:按高度分类,从小到大枚举最大高度,比当前枚举的高度 h 要高的,一定删,比它小的,如果删前 ? 小的。

贪心代码:

 1 #include<bits/stdc++.h>
 2 #define ll long long
 3 using namespace std;
 4 const int maxx=1e5+20;
 5 struct node{
 6     ll h,v,p;
 7     bool operator<(const node&a)const{
 8         return h<a.h;
 9     }
10 }tree[maxx];
11 ll B[220],arr[maxx],pre[maxx];//B储存已经遍历过的树在该价值的数量,arr储存第i高及比第i高的树都被砍掉的花费,pre储存第i高树矮的树的数量
12 int main()
13 {
14     ll n;
15     while(~scanf("%lld",&n)){
16         memset(B,0,sizeof(B));
17         for(int i=1;i<=n;i++)scanf("%lld%lld%lld",&tree[i].h,&tree[i].v,&tree[i].p);
18         sort(tree+1,tree+1+n);//从低到高排序高度
19         arr[n+1]=0;
20         for(int i=n;i>=1;i--)arr[i]=arr[i+1]+tree[i].v*tree[i].p;
21         pre[0]=0;
22         for(int i=1;i<=n;i++)pre[i]=pre[i-1]+tree[i].p;
23         ll ans=1e18;
24         for(int i=1,j=1;i<=n;i=j){//从小到大枚举最大高度
25             while(j<=n&&tree[j].h==tree[i].h)j++;
26             ll sums=0;
27             for(int k=i;k<j;k++)sums+=tree[k].p;
28             ll cost=arr[j];
29             if(sums<=pre[i-1]){//如果第i高的树的数量小于比它矮的树的数量,即将价值数组进行从小到大处理
30                 ll need=pre[i-1]-sums+1;
31                 for(int t=1;t<=200;t++){
32                     if(B[t]<=need){
33                         cost+=B[t]*t;
34                         need-=B[t];
35                     }else{
36                         cost+=need*t;
37                         break;
38                     }
39                 }
40             }
41             ans=min(ans,cost);
42             for(int k=i;k<j;k++)B[tree[k].v]+=tree[k].p;
43         }
44         printf("%lld
",ans);
45     }
46     return 0;
47 }

主席树代码:

#include <bits/stdc++.h>
#define LL long long
using namespace std;
const int maxx=2e5+100;
int tot;
struct Node{
    LL h,v,p;
    bool operator <(const Node &a)const
    {
        return a.h>h;
    }
}tree[maxx];
struct node{
    LL l,r,sum,num;
}p[maxx*40];
LL v[maxx],root[maxx];
LL update(LL rot,LL l,LL r,LL pos,LL num)//建立主席树
{
    LL book=++tot;
    p[book]=p[rot];//传递上一颗主席树的性质进行修改
    p[book].num+=(LL)num;
    p[book].sum+=(LL)pos*(LL)num;
    if(l==r) return book;
    LL mid=l+r>>1;
    if(pos<=mid) p[book].l=update(p[rot].l,l,mid,pos,num);
    else p[book].r=update(p[rot].r,mid+1,r,pos,num);
    return book;
}
LL query(LL rot,LL l,LL r,LL num)//查询
{
    if(l==r) return (LL)l*(LL)num;
    LL ans=p[p[rot].l].num;
    LL mid=l+r>>1;
    if(num<=ans) return query(p[rot].l,l,mid,num);
    else
    {
        return p[p[rot].l].sum+query(p[rot].r,mid+1,r,num-ans);
    }
}
int main()
{
    int n;
    while(~scanf("%lld",&n)){
        tot=0;
        memset(v,0,sizeof(v));
        for(int i=1;i<=n;i++){
            scanf("%lld%lld%lld",&tree[i].h,&tree[i].v,&tree[i].p);
        }
        sort(tree+1,tree+n+1);
        for(int i=1;i<=n;i++)
            v[i]=v[i-1]+tree[i].p;
        for(int i=1;i<=n;i++){
            root[i]=update(root[i-1],1,210,tree[i].v,tree[i].p);//主席树记录每种树的根节点
        }
        LL Minn=1e18,moneys=0;
        for(int i=n;i>=1;i--){
            LL treenum=0,sums=0;
            while(tree[i].h==tree[i-1].h){
                treenum+=tree[i].v*tree[i].p;
                sums+=tree[i].p;
                i--;
            }
            treenum+=tree[i].v*tree[i].p;
            sums+=tree[i].p;
            LL Smoney=0;
            if(v[i-1]>=sums)Smoney=moneys+query(root[i-1],1,210,v[i-1]-sums+1);//如果第i高的树的数量小于比它矮的树的数量,查询前i-1种树中的需要砍的树的花费
            else Smoney=moneys;
            Minn=min(Minn,Smoney);
            moneys+=treenum;//记录比第i种树高的树的砍掉的花费
        }
        printf("%lld
",Minn);
    }
    return 0;
}
原文地址:https://www.cnblogs.com/Aamir-Dan/p/11346330.html