链接:
https://ac.nowcoder.com/acm/contest/887/C
题意:
给n种树,其中每一种树都有高度h,每砍掉一棵树所需要的代价c,这种树的数量p。求花费最小的代价看砍树,使这群树中高度最大的树的数量大于树的总数量的一半。(砍树是需要直接把树砍完,不能砍一半~)
数据量:n<=1e5,h<=1e9,c<=200,p<=1e9
思路:
直接暴力的话,很明显是需要枚举每一种树为最高的树,然后去求需要砍的代价。直接暴力的话复杂度应该是(n* n)或者是 (n * c)。在这道题目中(n * c) 已经可以过了!不过太low,因为这道题c的数据量很明显可以出到1e9去的。我们就按照c的数据量是1e9来写(毕竟是训练)。
首先,依旧是需要枚举每一种树作为最高的树,然后需要判断代价,首先对树的高度从小到大排序,预处理后缀和,即s[i]表示到第i中树以及其之后的所有种类的树全部 砍掉之后需要的代价。还需要 建立权值线段树,维护权值为l~r这个区间的数的数量和代价和。然后从前向后进行枚举,到第i种树时,先把答案加上s[i+1]表示如果第i种树最高,那么它后面所有种类的树都要砍掉,然后去查询第i种树最高的情况下,前面最少需要砍掉树的代价是多少。需要注意的是多种树的高度相同的情况。
总的时间复杂度是O(n* log c)。
如果c到了1e9的时候 ,那么由于n最大是1e5,所以只需要离散化就能继续维护线段树了。(当然这道题目不需要)
比赛时队友做的这道题,当时在自闭另外一道题,这道题就只是大概读了一下题目,就没管了。比赛后敲了一下代码,感觉思路还挺简单,由于暴力都能过,所以成了一道签到题。但是后来比赛后我写线段树的时候wa了无数发,一是因为本来没有考虑 h相等的情况直接按照不相等处理是不对的,二是因为有一处int * int没有开 ll , 三是线段树中竟然有一个地方 = 写成了 == ,样例还莫名其妙的过了。。。
AC代码:
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
// #include
// #include
#define pb push_back
#define _filein freopen("C:\\Users\\zsk18\\Desktop\\in.txt","r",stdin)
#define _fileout freopen("C:\\Users\\zsk18\\Desktop\\out.txt","w",stdout)
#define ok(i) printf("ok%d\n",i)
using namespace std;
// using namespace __gnu_pbds;
typedef double db;
typedef long long ll;
typedef pair<int,int>PII;
const double PI = acos(-1.0);
// const ll MOD=1e9+7;
const ll NEG=1e9+6;
const int MAXN=5e5+10;
const int INF=0x3f3f3f3f;
const ll ll_INF=9223372036854775807;
const double eps=1e-9;
struct trees
{
ll h,c,p;
bool operator <(const trees &a)const{
return h<a.h;
}
}tt[MAXN];
struct segment_tree
{
ll l,r;
ll num;
ll w;
}t[MAXN];
int n;
ll s[MAXN];
void build(ll p,ll l,ll r)
{
t[p].l=l,t[p].r=r;
if(l==r)
{t[p].w=t[p].num=0;return;}
ll mid=(l+r)>>1;
build(p*2,l,mid);
build(p*2+1,mid+1,r);
t[p].num=t[p*2].num+t[p*2+1].num;
t[p].w=t[p*2].w+t[p*2+1].w;
return;
}
void change(ll p,ll tar,ll val)//p点的数量加上val个
{
if(t[p].l==t[p].r)
{
t[p].num+=val;
t[p].w+=(val*tar);
return;
}
ll mid=(t[p].l+t[p].r)>>1;
if(tar<=mid)change(p*2,tar,val);
else change(p*2+1,tar,val);
t[p].num=t[p*2].num+t[p*2+1].num;
t[p].w=t[p*2].w+t[p*2+1].w;
return;
}
ll ask(ll p,ll val)
{
if(val>=t[p].num)return t[p].w;
if(t[p].l==t[p].r)return min(val*t[p].l,t[p].w);
if(t[p*2].num>=val)return ask(p*2,val);
else return t[p*2].w+ask(p*2+1,val-t[p*2].num);
}
int main()
{
// _filein;
while(~scanf("%d",&n))
{
memset(t,0,sizeof(t));
memset(s,0,sizeof(s));
ll l=200,r=0;
for(int i=1;i<=n;i++)
{
scanf("%lld%lld%lld",&tt[i].h,&tt[i].c,&tt[i].p);
l=min((ll)l,tt[i].c);
r=max((ll)r,tt[i].c);
}
sort(tt+1,tt+n+1);
// ok(0);
for(int i=n;i>=1;i--)
{
s[i]=s[i+1]+(tt[i].c*tt[i].p);
}
build(1,l,r);
ll ans=ll_INF;
ll sum=0;
// ok(1);
for(int i=1;i<=n;i++)
{
int k;
ll now=0;
for(k=0;i+k<=n;k++)
{
if(tt[i].h!=tt[i+k].h)break;
now+=tt[i+k].p;
// printf("i=%d k=%d now=%I64d\n",i,k,now);
}
k--;
ll mid=0;
mid+=s[i+k+1];
// ok(3);
if(sum>=now)
mid+=ask(1,sum-now+1);
// printf("i=%d k=%d sum=%I64d now=%I64d mid=%I64d\n",i,k,sum,now,mid);
ans=min(ans,mid);
// ok(4);
sum+=now;
for(int j=i;j<=i+k;j++)
{
change(1,tt[j].c,tt[j].p);
}
i+=k;
}
printf("%lld\n",ans);
}
return 0;
}