题目链接:http://202.121.199.212/JudgeOnline/problem.php?id=1716

【题意】

1到N的区间,一种操作让编号从a到b的数变为z,但不会低于2,问多次操作后大于2的数减2后的和为多少。

【分析】

本来这题可以用线段树模拟过的,但是这里的N非常大,达到109,开个一维数组就会爆内存,更何况开个线段树。

分析题目后不难发现最后的一个操作一定生效,之前的操作如果有涉及之后操作区间的部分就会失效。根据这条性质,从操作的后面往前扫描,更新区间内的元素,如果元素被更新过就忽略,这样根据所有元素更新后的值就能算出结果了。如果用元素标记的方法一来时间复杂了,二来内存不够,所以绝对不能开一个一位数组来标记,也就是说这种方法不可行。

于是可以想到,把操作看成区间线段,从后往前坐做,每次只要更新之前未被区间覆盖到的元素,现在的问题是,区间可能交叉重叠,如何快速地判断区间是否已被覆盖?如果区间被之前的区间分为多个部分,如何区分?

以下为错误做法,但是AC了,数据太弱~

使用STL的MAP,map<int,int>表示 key之前的区间的元素值为value 。

对于每次的更新区间,处理其端点,二分查找与两个端点最近的且在右边的key值和相对应的value,来判断是否被覆盖,并更新。
     假设依次处理红蓝绿三个区间,先查找与a、c相近的key,都没找到,就认为不存在覆盖,访问下map[a],但值不动(如过之前没访问过就获得新值0,如果已经访问过,则保留原值,如果改变其值对端点重叠的情况会算错);map[c]=value1。处理蓝色时b找到c,map[c]的值存在,所以b到c的区间是被覆盖的,不处理,map[e]=value2。 同理[d,e]被覆盖,map[f]=value3。这样就被分出3个有效区间[a,c]=value1、[c+1,e]=value2、[e+1,f]=value3。还有些细节处理下,这个算法就能AC题目了。

代码:

  1. #include <stdio.h>
  2. #include <string.h>
  3. #include <iostream>
  4. #include <map>
  5. #include <algorithm>
  6. using namespace std;
  7. map<int,long long> su;
  8. int n,m;
  9. struct node
  10. {
  11. int a,b,z;
  12. void read()
  13. {
  14. scanf("%d%d%d",&a,&b,&z);
  15. }
  16. }data[];
  17. int main ()
  18. {
  19. while (~scanf("%d",&n))
  20. {
  21. su.clear();
  22. scanf("%d",&m);
  23. for (int i=;i<m;++i)
  24. data[i].read();
  25. for (int i=m-;i>=;--i)
  26. {
  27. map<int,long long>::iterator p1=su.upper_bound(data[i].a);
  28. map<int,long long>::iterator p2=su.upper_bound(data[i].b);
  29. int pp1,pp2;
  30. long long v1,v2;
  31. if (p1==su.end()) pp1=-; else {pp1=p1->first;v1=su[pp1];}
  32. if (p2==su.end()) pp2=-; else {pp2=p2->first;v2=su[pp2];}
  33.  
  34. if (pp1==- || v1<=)
  35. {
  36. //cout<<su[data[i].a]<<endl;
  37. su[data[i].a];
  38. if (pp1!=- && data[i].b+>pp1)su[pp1]=data[i].z; //这里特殊区间要判断排除下
  39. }
  40. if (pp2==- || v2<=)
  41. {
  42. su[data[i].b+]=data[i].z;
  43. }
  44.  
  45. }
  46. long long sum=;
  47. map<int,long long>::iterator i=su.begin();
  48. //cout<<i->first<<" "<<i->second<<endl;
  49. int pp=i->first;
  50. ++i;
  51. for (;i!=su.end();++i)
  52. {
  53. if (i->second>=)
  54. {
  55. // cout<<i->first<<" "<<i->second<<endl;
  56. int tem=i->first;
  57. if (tem>n+) tem=n+;
  58. sum+=(long long )(i->second-)*(long long)(tem-pp);
  59. }
  60. pp=i->first;
  61. }
  62. cout<<sum<<endl;
  63. }
  64. }

用map时要注意,只要访问修改过map的元素,原先的迭代器就有可能失效!

这种用map的算法因为用key的大小关系来标记线段,所以不仅可以标记离散的区间,还可以来标记连续的空间: map<double,int>,很是强大。

很可惜以上是错误的算法,因为有种情况该算法无法解决,那就是区间被之前的区间分为多个部分,该算法最多只能分出两部分,有的区间就漏了!

《正确做法》:

最朴素的做法,直接从前往后,对每个区间进行染色,这样就能覆盖老的值,现在只要加快区间染色速度就好。

做法和以上类似,用map<int,int>表示 key之前的区间的元素值为value,只是为了要覆盖原来的区间,所以要删除在现在区间内的所有key,这个二分查找上下界就好了。

  1. class Intervals {
  2. TreeMap<Integer, Integer> map = new TreeMap<Integer, Integer>();
  3. Intervals() {
  4. map.put(Integer.MIN_VALUE, 0);
  5. map.put(Integer.MAX_VALUE, 0);
  6. }
  7. void paint(int s, int t, int c) {
  8. int p = get(t);
  9. map.subMap(s, t).clear(); /******/
  10. map.put(s, c);
  11. map.put(t, p);
  12. }
  13. int get(int k) {
  14. return map.floorEntry(k).getValue();
  15. }
  16. long getAns(int n) {
  17. long ans = 0;
  18. for (int i = 1; i <= n; ) {
  19. long v = get(i);
  20. int r = map.higherKey(i);
  21. ans += v * (r - i);
  22. i = r;
  23. }
  24. return ans;
  25. }
  26. }

连续区间覆盖染色问题 ------ SHUOJ 1716的更多相关文章

  1. CODEVS 2171 棋盘覆盖

    2171 棋盘覆盖 给出一张nn(n<=100)的国际象棋棋盘,其中被删除了一些点,问可以使用多少12的多米诺骨牌进行掩盖. 错误日志: 直接在模板上调整 \(maxn\) 时没有在相应邻接表数 ...

  2. TZOJ 4602 高桥和低桥(二分或树状数组+二分)

    描述 有个脑筋急转弯是这样的:有距离很近的一高一低两座桥,两次洪水之后高桥被淹了两次,低桥却只被淹了一次,为什么?答案是:因为低桥太低了,第一次洪水退去之后水位依然在低桥之上,所以不算“淹了两次”.举 ...

  3. bzoj 1780

    这是一道环上的问题,我们先将一个环展开,再复制一次. 这样,任何一个合法方案一定对应在转换后的序列的一些连续的区间,使得它们的并的长度大于等于圈长. 然后,我们将区间合并一下(就是将一些被其他区间包含 ...

  4. zoj 1610 Count the Colors 【区间覆盖 求染色段】

    Count the Colors Time Limit: 2 Seconds      Memory Limit: 65536 KB Painting some colored segments on ...

  5. CODEVS1022 覆盖 (二分图染色+匈牙利算法)

    先对整幅图进行二分图染色,再跑一遍匈牙利算法. /* CODEVS1022 */ #include <cstdio> #include <cstring> #include & ...

  6. P2486 [SDOI2011]染色(树剖)区间覆盖+区间的连续段

    https://www.luogu.org/problemnew/show/P2486 值的一看https://www.cnblogs.com/Tony-Double-Sky/p/9283262.ht ...

  7. BZOJ 2243: [SDOI2011]染色 [树链剖分]

    2243: [SDOI2011]染色 Time Limit: 20 Sec  Memory Limit: 512 MBSubmit: 6651  Solved: 2432[Submit][Status ...

  8. [codevs1022]覆盖

    [codevs1022]覆盖 试题描述 有一个N×M的单位方格中,其中有些方格是水塘,其他方格是陆地.如果要用1×2的矩阵区覆盖(覆盖过程不容许有任何部分重叠)这个陆地,那么最多可以覆盖多少陆地面积. ...

  9. loj 1429(可相交的最小路径覆盖)

    题目链接:http://lightoj.com/volume_showproblem.php?problem=1429 思路:这道题还是比较麻烦的,对于求有向图的可相交的最小路径覆盖,首先要解决成环问 ...

随机推荐

  1. Redis基本操作-20150608

    Redis基本操作-20150608 [http://my.oschina.net/u/241255/blog/206991]   Redis是一个开源的使用ANSI C语言编写.支持网络.可基于内存 ...

  2. 去除移动端 a标签 点击有一个 阴影效果

    outline: none;appearance:none;  -webkit-tap-highlight-color: transparent;   

  3. ThreadPool for Delphi

    http://sourceforge.net/projects/threadpoolpas/ http://hivelocity.dl.sourceforge.net/project/threadpo ...

  4. CentOS6.5下安装MariaDB5.5.36

    yum groupinstall -y "Development Tools" yum install -y cmake openssl-devel zlib-devel yum ...

  5. 提升jQuery开发技能的教程

    iPhone-like Sliding Headers Simple jQuery Spy Effect Simple use of Event Delegation Adding Keyboard ...

  6. Codeforces Round #311 (Div. 2) D. Vitaly and Cycle 图论

    D. Vitaly and Cycle Time Limit: 20 Sec Memory Limit: 256 MB 题目连接 http://codeforces.com/contest/557/p ...

  7. 【好文翻译】一步一步教你使用Spire.Doc转换Word文档格式

    背景: 年11月,微软宣布作为ECMA国际主要合作伙伴,将其开发的基于XML的文件格式标准化,称之为"Office Open XML" .Open XML的引进使office文档结 ...

  8. linux C(undefined reference to `sqrt')

    那是因为没有链接到math库 可以这样来做,在后面加上-lm. 代码如下: gcc 10.c -o 10 -lm

  9. 在Android应用中实现Google搜索的例子

    有一个很简单的方法在你的 Android 应用中实现 Google 搜索.在这个例子中,我们将接受用户的输入作为搜索词,我们将使用到 Intent.ACTION_WEB_SEARCH . Google ...

  10. 黑客破译android开发代码真就那么简单?

    很多程序员辛辛苦苦开发出的android开发代码,很容易就被黑客翻译了. Google似乎也发现了这个问题,从SDK2.3开始我们可以看到在android-sdk-windows\tools\下面多了 ...