示例:
输入:
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; }