HDU 4217

点击打开题目链接

题型就是数据结构。给一个数组,然后又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;
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值