Governing sand
传送门:https://ac.nowcoder.com/acm/contest/887/C
来源:牛客网
题意:给了n种树,每种树的个数是p[i],高度是h[i],砍掉每个树花费是c[i]。要求最高的树要严格比总数目的一半多,问砍掉一些树使得条件满足的最小代价。
思路:这个题首先看到数据范围想到要枚举每一种树作为最高的树,这样必然要先把比它高的全都砍掉,排完序后这个可以通过一个后缀和解决,
然后就是砍掉一些比他小的树,使条件满足。
因为后缀和已经处理出砍掉了多少树,而且所有树的总数已知,那么砍掉多少棵树就很显然
K=sum-2*x+1
最后处理砍掉的树的代价最小,直接根据每棵树代价大小把它插入到应该在的位置,这样树状数组上的点的代价就是一个单调的,二分求砍到第几种树即可。
#include <bits/stdc++.h> using namespace std; typedef long long ll; const int N=2e5+5; const ll inf=0x3f3f3f3f3f3f3f; struct node { ll w; int pos,h,p; }a[N]; map<int,ll>m; map<int,int>zz; int n,b[N]; ll sum[N],num[N],cnt[N],C,W[N]; ll ask_w(int x) { ll ans=0; for(;x;x-=x&(-x)) ans+=sum[x]; return ans; } ll ask_num(int x) { ll ans=0; for(;x;x-=x&(-x)) ans+=num[x]; return ans; } void add_w(int x,ll y) { for(;x<=n;x+=x&(-x)) sum[x]+=y; } void add_num(int x,ll y) { for(;x<=n;x+=x&(-x)) num[x]+=y; } bool cmp(node x,node y) { return x.w<y.w; } bool cmp2(node x,node y) { return x.h<y.h; } int main() { while(~scanf("%d",&n)) { memset(num,0,sizeof num); memset(sum,0,sizeof sum); memset(W,0,sizeof W); memset(cnt,0,sizeof cnt); m.clear(); C=0; for(int i=1;i<=n;i++) { scanf("%d%lld%lld",&a[i].h,&a[i].w,&a[i].p); a[i].pos=i; } sort(a+1,a+1+n,cmp); for(int i=1;i<=n;i++) b[a[i].pos]=i; sort(a+1,a+1+n,cmp2); for(int i=1;i<=n;i++) { zz[b[a[i].pos]]=i; } ll ans=inf; ll las=0; for(int i=n;i>=1;i--) { m[a[i].h]+=a[i].p; if(i!=n&&a[i].h==a[i+1].h) { cnt[i]=cnt[i+1]; W[i]=W[i+1]; las+=a[i].p*a[i].w; } else { cnt[i]=cnt[i+1]+m[a[i+1].h]; W[i]=W[i+1]+las; las=a[i].p*a[i].w; } C+=a[i].p; } for(int i=1;i<=n;i++) { int l=0,r=n; ll tmp=W[i],k=max(0ll,C-cnt[i]-2*m[a[i].h]+1); if(a[i].h==a[i-1].h) { add_w(b[a[i].pos],a[i].w*a[i].p); add_num(b[a[i].pos],a[i].p); continue; } while(l<r) { int mid=(l+r)/2; if(ask_num(mid)>=k) { r=mid; } else l=mid+1; } tmp+=ask_w(max(0,l-1)); k-=ask_num(max(0,l-1)); tmp+=k*a[zz[l]].w; ans=min(ans,tmp); add_w(b[a[i].pos],a[i].w*a[i].p); add_num(b[a[i].pos],a[i].p); } printf("%lld ",ans); } return 0; }