题型就是数据结构。给一个数组,然后又k次操作,每次操作给定一个数ki, 从数组中删除第ki小的数,要求的是k次操作之后被删除的所有的数字的和。
简单的思路就是,用1标记该数没有被删除,0表示已经被删除,对于找到第ki小的数, 只需要找到标记数组中第一个前缀和为ki的下标,又因为用来标记的数组的前缀和是不减数列,所以可以用二分来加速。这里值得注意的是,被删除后的数,不会第二次或者多次被找到,即每个数最多被找到一次,因为如果该数被删除了,而且该数所在下标的前缀和是ki,那么一定还存在一个更小的下标,使得它的前缀和也是ki, 而我们要找的就是第一次出现前缀和为ki的下标。还需要使用I64.
附上代码:
/*************************************************************************
> File Name: 4217.cpp
> Author: Stomach_ache
> Mail: sudaweitong@gmail.com
> Created Time: 2014年04月26日 星期六 21时51分19秒
> Propose: HDU 4217
************************************************************************/
//BIT + BinarySearch 复杂度 O(k * logn * logn)
//单点更新,区间求值, 用1表示该数没有被删除,0表示该数已经被删除
#include <cmath>
#include <string>
#include <vector>
#include <cstdio>
#include <fstream>
#include <cstring>
#include <iostream>
#include <algorithm>
using namespace std;
#define X first
#define Y second
#define MAX_N (262144 + 5)
typedef long long LL;
typedef pair<int, int> pii;
int n, k;
int c[MAX_N];
int
lowbit(int x) {
return x & (-x);
}
LL
get_sum(int x) {
LL s = 0;
while (x > 0) {
s += c[x];
x -= lowbit(x);
}
return s;
}
void
update(int x, int v) {
while (x <= n) {
c[x] += v;
x += lowbit(x);
}
return ;
}
int
main(void) {
int T, cnt = 1;
scanf("%d", &T);
while (T--) {
scanf("%d %d", &n, &k);
memset(c, 0, sizeof(c));
for (int i = 1; i <= n; i++) {
update(i, 1);
}
LL ans = 0;
for (int i = 0; i < k; i++) {
int ki;
scanf("%d", &ki);
//只需要找到前缀和为ki对应的数,也就是第ki小的数
//此处为不减数列,所以使用二分来找到第一个前缀和为ki对应的数
int L = ki, R = n, tmp = L;
while (L <= R) {
int mid = L + (R - L) / 2;
int s = get_sum(mid);
if (s < ki) {
L = mid + 1;
} else {
if (s == ki) {
tmp = mid;
}
R = mid;
}
if (tmp == L && tmp == R) {
break;
}
}
ans += tmp;
update(tmp, -1);
}
printf("Case %d: %I64d\n", cnt++, ans);
}
return 0;
}