【2019牛客暑期多校训练营 第七场 C题】【Governing sand】【线段树】

题目链接:
https://ac.nowcoder.com/acm/contest/887/C
题意:
N N N颗树,第i种树有 P [ i ] P[i] P[i]颗,高度为 H [ i ] H[i] H[i],砍掉这种树代价为 C [ i ] C[i] C[i]
求最小的花费,使得最高的树的数量超过剩下树总数的一半
题解:
按高度从高到低进行排序,然后用权值线段树维护。
每个高度都查询砍完需要的树的最小花费,更新答案
每次查询完当前高度,砍完当前高度的树(更新权值线段树)
代码:

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define sz sizeof
#define lc u<<1
#define rc u<<1|1
#define m (l+r)/2
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef pair<int, int> Pair;
const int MAX = 1e5 + 10;

struct SegTree {
	int l, r;
	ll n, s;
}t[500 << 2];

void build(int u, int l, int r) {
	t[u].l = l, t[u].r = r, t[u].n = t[u].s = 0;
	if (l == r)return;
	build(lc, l, m); build(rc, m + 1, r);
}

void pushup(int u) {
	t[u].n = t[lc].n + t[rc].n;
	t[u].s = t[lc].s + t[rc].s;
}

void update(int u, ll c, ll n) {
	int l = t[u].l, r = t[u].r;
	if (l == r) {
		t[u].n += n;
		t[u].s += n * c;
		return;
	}
	if (c <= m)update(lc, c, n);
	else if (c > m)update(rc, c, n);
	pushup(u);
}

ll query(int u, ll last) {
	int l = t[u].l, r = t[u].r;
	if (last <= 0)return 0;
	if (l == r)return 1ll * last * l;
	if (t[lc].n >= last)return query(lc, last);
	else return t[lc].s + query(rc, last - t[lc].n);
}

struct node {
	ll h, c, n;
	bool operator < (const node& a)const {
		return h > a.h;
	}
}a[MAX];

int n;

int main() {
#ifdef ACM_LOCAL
	freopen("input.txt", "r", stdin);
	freopen("output.txt", "w", stdout);
#endif
	while (~scanf("%d", &n)) {
		build(1, 1, 200);
		ll tot = 0;
		for (int i = 1; i <= n; i++) {
			scanf("%lld%lld%lld", &a[i].h, &a[i].c, &a[i].n);
			update(1, a[i].c, a[i].n);
			tot += a[i].n;
		}
		sort(a + 1, a + 1 + n);
		ll minn = 1e18, pre_n = 0, pre_s = 0, now_n, now_s;
		for (int i = 1; i <= n; i++) {
			now_n = now_s = 0;
			while (i < n && a[i].h == a[i + 1].h) {
				update(1, a[i].c, -a[i].n);
				now_n += a[i].n;
				now_s += a[i].n * a[i].c;
				i++;
			}
			update(1, a[i].c, -a[i].n);
			now_n += a[i].n;
			now_s += a[i].n * a[i].c;
			minn = min(minn, query(1, tot - pre_n - 2 * now_n + 1) + pre_s);
			pre_s += now_s;
			pre_n += now_n;
		}
		printf("%lld\n", minn);
	}
	return 0;
}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值