枚举+树状数组(经典)
题意:有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; }