题意:砍树,目的让最高的树数量大于所有树的数量。,砍树有代价,求最小代价
题解:对高度排序,枚举每个最高高度,第一部分肯定是删除大于枚举高度的的所有代价,第二步分就是在小于枚举高度所有树在删除足够的就可以了。所以用个数据结构,权值线段树找第K大,返回第K大时的花费就好了。
细节:仔细读题,题中给的是每种树的高度对应树代价和树的数量,所以会有同种高度但是代价不同的的情况。第二点就是注意边更新边查询,树中不能出现大于枚举高度的值。
#include<cstdio>
#include<cstring>
#include<string.h>
#include<algorithm>
#include<iostream>
#include<stdlib.h>
#include<vector>
using namespace std;
const int INF=0x3f3f3f3f;
const int maxn=2e5+10;
typedef long long ll;
const ll inf=0x3f3f3f3f3f3f3f;
ll sum[maxn*4];//该结点为这段区间的和
ll num[maxn*4];//该结点的某个数的权值(出现次数)
void build(int l,int r,int p){
if(l==r){
sum[p]=0;
num[p]=0;
return ;
}
int mid=(l+r)/2;
build(l,mid,p<<1);
build(mid+1,r,p<<1|1);
}
void update(int l,int r,int pos,int p,ll nm){
if(l==r){
sum[p]+=pos*nm;
num[p]+=nm;
return ;
}
int mid=(l+r)/2;
if(pos<=mid) update(l,mid,pos,p<<1,nm);
else update(mid+1,r,pos,p<<1|1,nm);
sum[p]=sum[p<<1]+sum[p<<1|1];
num[p]=num[p<<1]+num[p<<1|1];
}
ll query(int l,int r,ll k,int p){//优先左子树找
if(l==r){
ll kk=min(num[p],k);
return kk*l;
}
int mid=(l+r)/2;
ll Lnum=num[p<<1];
if(Lnum>=k){//如果左子树和已经大于要凑的数,直接去左子树
return query(l,mid,k,p<<1);
}else{//负责那一定是左子树所有数出现的次数+右子树的情况
return sum[p<<1]+query(mid+1,r,k-Lnum,p<<1|1);
}
}
struct node{
ll h,w,num;
}s[maxn];
bool cmp(node a,node b){
return a.h<b.h;
}
ll presum[maxn];//花费
ll pre[maxn];//树的个数
int main(){
std::ios::sync_with_stdio(false);
int n;
while(~scanf("%d",&n)){
memset(num,0,sizeof(num));
memset(sum,0,sizeof(sum));
int N=200;
build(1,N,1);
ll chk=0;
for(int i=1;i<=n;i++){
scanf("%lld%lld%lld",&s[i].h,&s[i].w,&s[i].num);
chk+=s[i].num;
}
sort(s+1,s+n+1,cmp);
for(int i=1;i<=n;i++){
presum[i]=presum[i-1]+s[i].num*s[i].w;
pre[i]=pre[i-1]+s[i].num;
}
ll ans=inf;
for(int i = 1; i <= n; i++){
ll hh = s[i].h;
ll nn = s[i].num;
int flag = 0;
int j = i+1;
for(; j <= n; j++){
if(hh == s[j].h){
nn += s[j].num;
flag = 1;
}
else break;
}
j--;
ll ans_2 = presum[n] - presum[j];
ll cnt_2 = pre[n] - pre[j];
if(nn > ((chk - cnt_2 + 1)/2)) ans = min(ans, ans_2);
else{
ll p = chk-cnt_2;
ll k = (p - nn) - nn + 1;
ll ans_1 = query(1, N, k, 1);
ans = min(ans_1 + ans_2, ans);
}
for(int pp=i;pp<=j;pp++){
update(1, N, s[pp].w, 1, s[pp].num);
}
if(flag) i = j;
}
printf("%lld\n", ans);
}
return 0;
}
/*
3
3 20 1
2 10 3
4 5 4
3
4 100 2
4 100 4
4 100 3
*/