2019牛客暑期多校训练营(第七场场)_C题Governing sand(权值线段树+枚举)

题目链接: https://ac.nowcoder.com/acm/contest/887/C

题意: 给你n种树,以及它的高度,被砍掉的代价,以及它的数量,问你,砍掉一些树后,使得剩下的树中,最高的树的数量是剩下总数的一半 + 1 以上!
思路: 很明确,直接枚举,从最高的树往低的树枚举,然后用权值线段树来维护相应树的数量以及相应的代价!

代码

因为没写过权值线段树的代码,所以到师傅那学了下,自己手敲了一遍,wa了几发,离散化下表忘记加1了,代码有点乱希望别介意!

#include 
using namespace std;
const int maxn = 1e5 + 5;
const long long INF = 0xffffffffffffff;
struct node
{
    long long num;
    long long val;
}nodes[maxn<<2];

struct tree
{
     long long height;
     long long val;
     long long num;

     friend bool operator < (tree a, tree b)
     {
         return a.height > b.height;
     }
}em[maxn];

//用于离散化
vector<long long> ve;
inline int getID(long long x)
{
    return (lower_bound(ve.begin(), ve.end(), x) - ve.begin()) + 1;
}

void init()
{
    ve.clear();
}

void PushUp(int root)
{
    nodes[root].num = nodes[root<<1].num + nodes[root<<1|1].num;
    nodes[root].val = nodes[root<<1].val + nodes[root<<1|1].val;
}
//建树
void Build(int root, int l, int r)
{
    //记得每个节点都要初始化
    nodes[root].num = nodes[root].val = 0;
    if(l == r){
        return ;
    }
    int mid = (l + r) >> 1;

    Build(root<<1, l, mid);
    Build(root<<1|1, mid + 1, r);
}

void add(int root, int l, int r, int k, long long val)
{
    if(l == r){
        nodes[root].num += val;
        nodes[root].val += ve[l - 1] * val;

        return ;
    }

    int mid = (l + r) >> 1;

    if(k <= mid){
        add(root<<1, l , mid, k, val);
    }
    else{
        add(root<<1|1, mid + 1, r, k, val);
    }

    PushUp(root);
}
long long query(int root, int l, int r, long long neednum)
{
    if(neednum <= 0) return 0;  // 不用砍树了 当然返回0

    if(l == r){
        return ve[l - 1] * neednum;
    }

    int mid = (l + r) >> 1;

    long long ans = 0;

    if(neednum < nodes[root<<1].num){
        ans += query(root<<1, l, mid, neednum);
    }
    else{
        ans += nodes[root<<1].val;
        ans += query(root<<1|1, mid + 1, r, neednum - nodes[root<<1].num);
    }

    return ans;
}

int main()
{
    ios::sync_with_stdio(false);
    int n;
    while(cin >> n){
        init();
        for(int i = 1; i <= n; i++){
            cin >> em[i].height >> em[i].val >> em[i].num;
            ve.push_back(em[i].val);
        }
        sort(em + 1, em + 1 + n);
        sort(ve.begin(), ve.end());
        ve.erase(unique(ve.begin(), ve.end()), ve.end());

        Build(1, 1, n);

        long long totnum = 0;
        for(int i = 1; i <= n; i++){
            totnum += em[i].num;
            add(1, 1, n, getID(em[i].val), em[i].num);      //离散化后把树的数量代价给加进去
        }

        long long ans = INF, now_num = 0, now_val = 0, nums = 0, vals = 0;

        for(int i = 1; i <= n; i++){
            now_num += em[i].num;
            now_val += em[i].val * em[i].num;

            if(i != n - 1 && em[i].height == em[i + 1].height){
                add(1, 1, n, getID(em[i].val), -em[i].num);
                continue;
            }
            add(1, 1, n, getID(em[i].val), -em[i].num);    // 砍掉这类树

            long long neednum = totnum - (now_num * 2 - 1); //比一半多

            ans = min(ans, vals + query(1, 1, n, neednum));

            vals += now_val;
            totnum -= now_num;
            now_val = now_num = 0;

            if(vals > ans){
                break;
            }
        }
        cout << ans << endl;
    }
    return 0;
}

你可能感兴趣的:(ACM)