您的位置:首页 > 理论基础 > 数据结构算法

SGU 311. Ice-cream Tycoon 树状数组

2014-08-17 20:37 253 查看
题目链接点这儿

两种操作

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
, total_sum
, total_cnt
;

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;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息