2019牛客暑期多校训练营(第七场)

C题:

  • 题意:给出n种树,每一种树对应了高度,砍掉一颗需要的钱,和数量。现在问,花费最少的钱,使得所有高度最高的树的和大于阳现在树的总和的一半(严格)
  • 采用权值线段树写。我们考虑一下,树的高度等于x时,我们该如何计算当前的费用。首先统计所有树的高度等于x的,那么这个时候x是最高的,所以大于x的全部砍掉,而小于x的树,我们只需要砍到数量等于x-1即可。
    那我们就可以首先根据高度排序,那么比当前高的树砍掉,费用等同于求后缀。所以问题关键在于动态维护比高度x小的树,该怎么砍。
    我们开一颗以cost作为节点的权值线段树,维护当前高度,所有树在cost位置上的nums之和,那么维护高度x小的树的时候,我们只用贪心的从左边,即cost小的位置,开始取,一共取sum-x+1个即可,这就是砍掉的最小值了。
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
//#include
#include
#define up(i,a,b)  for(int i=a;i
#define dw(i,a,b)  for(int i=a;i>b;i--)
#define upd(i,a,b) for(int i=a;i<=b;i++)
#define dwd(i,a,b) for(int i=a;i>=b;i--)
//#define local
typedef long long ll;
const double esp = 1e-6;
const double pi = acos(-1.0);
const int INF = 0x3f3f3f3f;
const int inf = 1e9;
using namespace std;
int read()
{
    char ch = getchar(); int x = 0, f = 1;
    while (ch<'0' || ch>'9') { if (ch == '-')f = -1; ch = getchar(); }
    while (ch >= '0' && ch <= '9') { x = x * 10 + ch - '0'; ch = getchar(); }
    return x * f;
}
typedef pair<int, int> pir;
struct node { ll cost, num; };
int n;
const int N = 205;
ll nums[4 * N], cost[4 * N];
vector<int >vec;
void pushup(int root)
{
    nums[root] = nums[root << 1] + nums[root << 1 | 1];
    cost[root] = cost[root << 1] + cost[root << 1 | 1];
}
void update(int root, int l, int r, int pos, ll k)
{
    if (l == r)
    {
        nums[root] += k;
        cost[root] += k * 1ll * pos;
        return;
    }
    int mid = (l + r) >> 1;
    if (pos > mid)update(root << 1 | 1, mid + 1, r, pos, k);
    else update(root << 1, l, mid, pos, k);
    pushup(root);
}
ll querry(int root, int l, int r,ll k)
{
    if (k <= 0)return 0;
    if (l == r)
    {
        return 1ll * l*k;
    }
    int mid = (l + r) >> 1;
    if (nums[root << 1] >= k)
    {
        return querry(root << 1, l, mid, k);
    }
    else
    {
        return cost[root << 1] + querry(root << 1 | 1, mid + 1, r, k - nums[root << 1]);
    }
}
int main()
{
    while (cin >> n)
    {
        map < ll, vector < node > > mp;
        memset(cost, 0, sizeof(cost));
        memset(nums, 0, sizeof(nums));
        vec.clear();
        up(i, 0, n)
        {
            int x, y, z;
            x = read(), y = read(), z = read();
            mp[x].push_back(node{ y,z });
            vec.push_back(x);
        }
        sort(vec.begin(), vec.end());
        vec.erase(unique(vec.begin(), vec.end()), vec.end());
        ll sum = 0;
        for (auto i : vec)
        {
            for (auto k : mp[i])
            {
                update(1, 1, 200, k.cost, k.num);
                sum += k.num;
            }
        }
        ll ans = 1e18;
        ll sufix = 0;
        dwd(i, vec.size()-1, 0)
        {
            ll tempsum = 0;
            ll tempcost = 0;
            for (auto k : mp[vec[i]])
            {
                tempsum += k.num;
                tempcost += 1ll * k.num*k.cost;
                update(1, 1, 200, k.cost, -k.num);
            }
            sum -= tempsum;
            ans = min(ans, sufix + querry(1, 1, 200, sum - tempsum + 1));
            sufix += tempcost;
        }
        cout << ans << endl;
    }
    return 0;
}

你可能感兴趣的:(套题,线段树)