HDU 4509 湫湫系列故事——减肥记II(线段树-区间覆盖 或者 暴力技巧)
http://acm.hdu.edu.cn/showproblem.php?pid=4509
题目大意:
中文意义,应该能懂。
解题思路:
因为题目给的时间是一天24小时,而且还有分钟。为了解题方便,我们将小时换成分钟,那么一天24小时,总共有1440分钟。顾我就可以把一天里的任意HH:MM时间换成分钟。就这样一天的时间就变成[0,1440]区间了。
因为所给的活动最多是5*10^5,如果把活动的时间在线段[0,1440]都修改,那么时间的复杂度最坏是O(5*10^5*1440)。
(1)方法一:线段树-区间覆盖。
线段树参考的是《算法入门经典训练指南》刘汝佳 P199
线段树其中一个特性,区间修改。我们把[0,1440]看成一个区间,我们只需要把活动时间的在线段树中标记就行。因为线段树是区间,每次标记都是区间。所以时间复杂度最坏O(5*10^5*log(1440)),我们只要查询[0,1440]整个区间,统计被覆盖的时间ans,用1440-ans=最后的答案。
查询时间的复杂度最坏是整棵树的节点O(2*1440)。
所以总时间复杂度最坏是O(5*10^5*log(1440))+ O(2*1440)。
注意:线段树的真正区间[1,1440],叶子节点表示是1秒的时间。
(2)方法二:暴力技巧
这个方法是借鉴别人的。
为了好理解这个方法,我们先举个简单的例子。
我们假设区间是[0,8]。
输入的数据是:
3
1 4
3 5
6 7
求未覆盖的线段和的长度。答案是3。
具体方法是。当输入1 4时,将1出+1,表示1后面的区间被覆盖了1次,在4出-1,表示4后面的区间取消覆盖1次。 这样就表示了1 4区间被覆盖了。3 5和6 7也是同理。
怎么求那些区间没有覆盖了,我是从0开始到结束,看每个小区间是否被覆盖。用一个count记录,若count为零时,表示此节点没有被覆盖。
区间修改的时间复杂度是O(5*10^5*1),区间查询是整个区间时间复杂度是O(1440)
顾总时间复杂度是O(5*10^5*1)+O(1440)
AC代码:
线段树
1 #include<cstdio> 2 #include<cstring> 3 4 #define MAXN 1441//时间最多是1440 5 6 bool stree[MAXN << 2];//线段树 7 8 int L, R;//要覆盖的区间为[L,R] 9 void updata(int o, int lc, int rc){//区间覆盖 10 if(stree[o]) return ;//此区间被覆盖了 11 if(L <= lc && rc <= R){//区间[lc,rc]在被覆盖的范围 12 stree[o] = true; 13 return ; 14 } 15 int mid = (lc + rc) >> 1; 16 if(L <= mid) updata(o * 2 + 1, lc, mid); 17 if(mid < R) updata(o * 2 + 2, mid + 1, rc); 18 } 19 20 int ans;//统计被覆盖的时间和 21 void query(int o, int lc, int rc){ 22 if(stree[o]){ 23 ans += rc - lc + 1; 24 return ; 25 } 26 27 if(lc == rc) return ;//访问到叶子节点了 28 29 int mid = (lc + rc) >> 1; 30 query(o * 2 + 1, lc, mid); 31 query(o * 2 + 2, mid + 1, rc); 32 } 33 34 int main(){ 35 int n, hh[2], mm[2]; 36 while(~scanf("%d", &n)){ 37 38 memset(stree, 0, sizeof(stree)); 39 while(n--){ 40 scanf("%d:%d %d:%d", &hh[0], &mm[0], &hh[1], &mm[1]); 41 mm[0] += hh[0] * 60 + 1; 42 mm[1] += hh[1] * 60; 43 L = mm[0]; R = mm[1]; 44 updata(0, 1, 1440);//区间修改 45 } 46 ans = 0; 47 query(0, 1, 1440);//区间访问 48 printf("%d\n", 1440 - ans); 49 } 50 return 0; 51 }
暴力技巧:
1 #include<cstdio> 2 #include<cstring> 3 4 #define MAXN 1441 5 6 int time[MAXN]; 7 8 int main(){ 9 int n, hh[2], mm[2]; 10 while(~scanf("%d", &n)){ 11 12 memset(time, 0, sizeof(time)); 13 while(n--){ 14 scanf("%d:%d %d:%d", &hh[0], &mm[0], &hh[1], &mm[1]); 15 mm[0] += hh[0] * 60; 16 mm[1] += hh[1] * 60; 17 18 ++time[mm[0]]; --time[mm[1]]; 19 } 20 21 int ans = 0, count = 0; 22 for(int i = 0; i < 1440; ++i){ 23 count += time[i]; 24 if(count == 0) ++ans; 25 } 26 printf("%d\n", ans); 27 } 28 return 0; 29 }