HDU 4217

点击打开题目链接

题型就是数据结构。给一个数组,然后又k次操作,每次操作给定一个数ki, 从数组中删除第ki小的数,要求的是k次操作之后被删除的所有的数字的和。 

简单的思路就是,用1标记该数没有被删除,0表示已经被删除,对于找到第ki小的数, 只需要找到标记数组中第一个前缀和为ki的下标,又因为用来标记的数组的前缀和是不减数列,所以可以用二分来加速。这里值得注意的是,被删除后的数,不会第二次或者多次被找到,即每个数最多被找到一次,因为如果该数被删除了,而且该数所在下标的前缀和是ki,那么一定还存在一个更小的下标,使得它的前缀和也是ki, 而我们要找的就是第一次出现前缀和为ki的下标。还需要使用I64.


附上代码:

 1 /*************************************************************************
 2     > File Name: 4217.cpp
 3     > Author: Stomach_ache
 4     > Mail: sudaweitong@gmail.com
 5     > Created Time: 2014年04月26日 星期六 21时51分19秒
 6     > Propose: HDU 4217  
 7  ************************************************************************/
 8 //BIT + BinarySearch 复杂度 O(k * logn * logn)
 9 //单点更新,区间求值, 用1表示该数没有被删除,0表示该数已经被删除
10 #include <cmath>
11 #include <string>
12 #include <vector>
13 #include <cstdio>
14 #include <fstream>
15 #include <cstring>
16 #include <iostream>
17 #include <algorithm>
18 using namespace std;
19 
20 #define X first
21 #define Y second
22 #define MAX_N (262144 + 5)
23 typedef long long LL;
24 typedef pair<int, int> pii;
25 int n, k;
26 int c[MAX_N];
27 
28 int
29 lowbit(int x) {
30         return x & (-x);
31 }
32 
33 LL
34 get_sum(int x) {
35         LL s = 0;
36         while (x > 0) {
37                 s += c[x];
38                 x -= lowbit(x);
39         }
40 
41         return s;
42 }
43 
44 void 
45 update(int x, int v) {
46         while (x <= n) {
47                 c[x] += v;
48                 x += lowbit(x);
49         }
50 
51         return ;
52 }
53 
54 int
55 main(void) {
56         int T, cnt = 1;
57         scanf("%d", &T);
58         while (T--) {
59                 scanf("%d %d", &n, &k);
60                 memset(c, 0, sizeof(c));
61                 for (int i = 1; i <= n; i++) {
62                         update(i, 1);
63                 }
64                 LL ans = 0;
65                 for (int i = 0; i < k; i++) {
66                         int ki;
67                         scanf("%d", &ki);
68                         //只需要找到前缀和为ki对应的数,也就是第ki小的数
69                         //此处为不减数列,所以使用二分来找到第一个前缀和为ki对应的数
70                         int L = ki, R = n, tmp = L;
71                         while (L <= R) {
72                                 int mid = L + (R - L) / 2;
73                                 int s = get_sum(mid);
74                                 if (s < ki) {
75                                         L = mid + 1;
76                                 } else {
77                                         if (s == ki) {
78                                                 tmp = mid;
79                                         }
80                                         R = mid;
81                                 }
82                                 if (tmp == L && tmp == R) {
83                                         break;
84                                 }
85                         }
86                         ans += tmp;
87                         update(tmp, -1);
88                 }
89                 printf("Case %d: %I64d\n", cnt++, ans);
90         }
91 
92         return 0;
93 }

 


 

posted on 2014-04-30 18:04  Stomach_ache  阅读(172)  评论(0编辑  收藏  举报

导航