枚举+树状数组(经典)

题意:有n种树,给出每种数的高度、移除的花费和数量,求最小花费是多少使得剩下树中最高的树的数量占一半以上。

分析:以高度排序,枚举每一高度,对当前枚举到的这个高度视为最高高度,数目由num棵,然后后面肯定要砍掉因为比他高,然后再考虑比他小的,因为要构成俩倍的关系,所以最多保留num-1棵。

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N=205;
const int M=1e5+5;
ll tree[N][2];
struct node{
    ll h,p,c;
}a[M];
ll sufc[M],sufp[M];
bool cmp(node p,node q){
    return p.h<q.h;
}
void add(int u,ll x,int i){
    while(u<=200){
        tree[u][i]+=x;
        u+=-u&u;
    }
}
ll SUM(int u,int i){
    ll ans=0ll;
    while(u){
        ans+=tree[u][i];
        u-=-u&u;
    }
    return ans;
}
int main(){
    int n;
    while(~scanf("%d",&n)){
        memset(tree,0ll,sizeof(tree));
        ll sum=0,maxc=0,minc=M;
        for(int i=1;i<=n;i++){
            scanf("%lld%lld%lld",&a[i].h,&a[i].c,&a[i].p);
            sum+=a[i].p;
            maxc=max(maxc,a[i].c);
            minc=min(minc,a[i].c);
        }
        sort(a+1,a+1+n,cmp);
        for(int i=n;i>=1;i--){
            sufc[i]=sufc[i+1]+a[i].p*a[i].c;//后缀代价总和
            sufp[i]=sufp[i+1]+a[i].p;//后缀树木总数和 
        }
        ll ans=sufc[1];
        int i=1;
        /*for(int i=1;i<=n;i++)
            cout<<sufc[i]<<" ";
        cout<<endl;*/
        while(i<=n){
            int l=i,r=i;
            ll num=0ll,needcost=0ll;
            while(r<=n&&a[r].h==a[l].h)
                num+=a[r++].p;
            
            needcost+=sufc[r];
            ll need=sum-num-sufp[r]-(num-1);//枚举到当前认定为最高高度,比当前高的肯定要砍掉,然后再考虑前面因为要俩倍,所以最多砍掉num-1棵树 
            if(need<=0)
                ans=min(ans,needcost);
            else{
                ll L=minc,R=maxc,s=0ll;
                while(L<=R){
                    ll midd=(L+R)>>1;
                    if(SUM(midd,0)>=need)
                        s=midd,R=midd-1;
                    else
                        L=midd+1;
                }
                need-=SUM(s,0);
                needcost+=SUM(s,1);
                //因为要保持贪心,所以出现前缀和大于要砍的树的时候要减掉多出来的代价 
                needcost-=s*abs(need);
            }
            ans=min(ans,needcost); 
            while(l<r){
                add(a[l].c,a[l].p,0);
                add(a[l].c,a[l].c*a[l].p,1);
                l++;
            }
            i=r;//cout<<r<<endl;
        }
        printf("%lld\n",ans);
    }
    return 0;
}
View Code

 

posted @ 2019-08-10 05:33  starve_to_death  阅读(726)  评论(0编辑  收藏  举报