SGU 311. Ice-cream Tycoon 树状数组

题目链接点这儿

两种操作

1° 数i增加j个(1<=i, j<=1e6)

2° 查询前n个数的和是否小于等于k,如果小于则将它们删除。

操作数<=1e5

我们使用两个树状数组total_cnt, total_sum记录price<=i的时候的数的总数以及其总和

由于和前k个有关,我们就可以使用树状数组快速寻找getsum(i) < k的最大的 i里的方法,使用find(k)快速找到getCnt(i)<k的最大i,然后n-getCnt(i)就是需要的大小为i+1的数的个数。这时显然如果getSum(i) + (i+1) * (n - getCnt(i)) <= k的话,那么前n个数的和就是满足条件的。然后我们再做树状数组的更新操作即可完成删除操作。

代码见下

#include <bits/stdc++.h>

typedef long long ll;

const int n = 1000001;
ll cnt[n], total_sum[n], total_cnt[n];

void update(int pos, ll val) {
    cnt[pos] += val;
    for(int i = pos; i < n; i += i & -i)
        total_cnt[i] += val, total_sum[i] += pos*val;
}

ll getsum(int pos) {
    ll ret = 0;
    while(pos > 0) ret += total_sum[pos], pos -= pos & -pos;
    return ret;
}

int calc(const int n) {
	int ret = 0;
	while((1<<ret) + 1 < n) ret++;
	return ret;
}

std::pair<int, ll> find(ll t) {
	static int upper = calc(n);
	int ret1 = 0;
	for(int i = upper; i >= 0; i--) {
		ret1 += 1 << i;
		if(ret1 >= n || total_cnt[ret1] >= t) ret1 -= 1 << i;
		else t -= total_cnt[ret1];
	}
	return std::make_pair(ret1, t);
}

int main() {
	char str[7];
	int num;
	ll price;
	while(scanf("%s%d%I64d", str, &num, &price) > 0) {
		if(*str == 'A')
			update(price, num);
		else {
			std::pair<int, ll> ret = find(num);
			ll sum = getsum(ret.first);
			if(ret.first + 1 < n && sum + (ret.first+1)*ret.second <= price) {
				puts("HAPPY");
				while(num) {
					int pos = find(1).first + 1, d = std::min((ll)num, cnt[pos]);
					update(pos, -d);
					num-=d;
				}
			} else {
				puts("UNHAPPY");
			}
		}
	}
	return 0;
}


你可能感兴趣的:(数据结构,算法,ACM,树状数组,sgu)