• 连续区间覆盖染色问题 ------ SHUOJ 1716


    题目链接: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[200003];
    17 int main ()
    18 {
    19    while (~scanf("%d",&n))
    20    {
    21        su.clear();
    22        scanf("%d",&m);
    23        for (int i=0;i<m;++i)
    24         data[i].read();
    25        for (int i=m-1;i>=0;--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=-1; else {pp1=p1->first;v1=su[pp1];}
    32            if (p2==su.end()) pp2=-1; else {pp2=p2->first;v2=su[pp2];}
    33  
    34            if (pp1==-1 || v1<=2)
    35            {
    36                //cout<<su[data[i].a]<<endl;
    37                su[data[i].a];
    38                if (pp1!=-1 && data[i].b+1>pp1)su[pp1]=data[i].z; //这里特殊区间要判断排除下
    39            }
    40             if (pp2==-1 ||  v2<=2)
    41            {
    42                su[data[i].b+1]=data[i].z;
    43            }
    44  
    45        }
    46        long long sum=0;
    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>=2)
    54            {
    55               // cout<<i->first<<" "<<i->second<<endl;
    56               int tem=i->first;
    57               if (tem>n+1) tem=n+1;
    58                sum+=(long long )(i->second-2)*(long long)(tem-pp);
    59            }
    60            pp=i->first;
    61        }
    62        cout<<sum<<endl;
    63    }
    64 }
    View Code

        用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     }
    View Code


        

  • 相关阅读:
    netty ByteToMessageDecoder 分析
    netty 编/解码处理
    MAC 入门
    netty 学习
    php ioc and web rest design
    spring 启动流程
    淘宝美衣人
    ecslipe cdt lib link
    阿里巴巴中间件团队招人了!
    架构师速成-架构目标之伸缩性安全性
  • 原文地址:https://www.cnblogs.com/wuminye/p/3505412.html
Copyright © 2020-2023  润新知