2019暑期训练——牛客第七场 C. Governing sand(权值线段树)

Governing sand

链接:
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<bits/stdc++.h>
#include<algorithm>
#include<complex>
#include<iostream>
#include<iomanip>
#include<ostream>
#include<cstring>
#include<string.h>
#include<string>
#include<cstdio>
#include<cctype>
#include<vector>
#include<cmath>
#include<queue>
#include<set>
#include<stack>
#include<map>
#include<cstdlib>
#include<time.h>
#include<ctime>
#include<bitset>
// #include<ext/pb_ds/assoc_container.hpp>
// #include<ext/pb_ds/hash_policy.hpp>
#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;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值