• ACM-线段树区间更新+离散化


    区间更新与单点更新最大的不同就在于Lazy思想:

    http://blog.sina.com.cn/s/blog_a2dce6b30101l8bi.html

    可以看这篇文章,讲得比较清楚

    在具体使用上,因为是成段更新,目标区间内所有区间都需要更新,所以update时可以专门去找区间,不用一个个找点。所以可以不用node保存每个点左右范围,用a[]保存值,col[]保存标记反而比较方便

    区间替换和区间增减在我的http://www.cnblogs.com/qlky/p/5690265.html中都写了,这里讲一下离散化:

    离散化就是有时n个点的数据范围过大,或者过于分散。我们将节点映射到1-n中可以简化问题。基本过程如下:

    • 记录每个点的左端和右端,全部保存到一个数组a中并排序
    • 节点去重
    • 如果两个节点间距离大于1,添加一个中间节点
    • 再次对a排序
    • 在a中二分搜索原来每个点的左右端,将索引值保存在线段树中

    示例代码:

    sf("%d",&n);
            int cnt = 0,len = 1;
            for(i=1;i<=n;i++)//记录头尾
            {
                sf("%d %d",&s1[i],&s2[i]);
                a[++cnt] = s1[i];
                a[++cnt] = s2[i];
            }
            sort(a+1,a+1+cnt);
    
            for(i=2;i<=cnt;i++)//去重
            {
                if(a[i]!=a[i-1]) a[++len] = a[i];
            }
    
            for(i=len;i>1;i--)//添加中间值
            {
                if(a[i]-a[i-1]>1) a[++len] = a[i]-1;
            }
            sort(a+1,a+1+len);
    
            for(i=1;i<=n;i++)
            {
                int l = BSearch(1,len,s1[i]);
                int r = BSearch(1,len,s2[i]);
                update(i,l,r,1,len,1);
            }

    以poj 2528为例:

    http://blog.csdn.net/non_cease/article/details/7383736

    题意:n(n<=10000)个人依次贴海报,给出每张海报所贴的范围li,ri(1<=li<=ri<=10000000)。

          求出最后还能看见多少张海报。

    输入:

    1
    5
    1 4
    2 6
    8 10
    3 4
    7 10

    解法:离散化,如下面的例子(题目的样例),因为单位1是一个单位长度,将下面的

          1   2   3   4  6   7   8   10

         —  —  —  —  —  —  —  —

          1   2   3   4  5   6   7   8

    离散化  X[1] = 1; X[2] = 2; X[3] = 3; X[4] = 4; X[5] = 6; X[7] = 8; X[8] = 10

    于是将一个很大的区间映射到一个较小的区间之中了,然后再对每一张海报依次更新在宽度为1~8的墙上(用线段树),最后统计不同颜色的段数。

    但是只是这样简单的离散化是错误的,

    如三张海报为:1~10 1~4 6~10

    离散化时 X[ 1 ] = 1, X[ 2 ] = 4, X[ 3 ] = 6, X[ 4 ] = 10
    第一张海报时:墙的1~4被染为1;
    第二张海报时:墙的1~2被染为2,3~4仍为1;
    第三张海报时:墙的3~4被染为3,1~2仍为2。
    最终,第一张海报就显示被完全覆盖了,于是输出2,但实际上明显不是这样,正确输出为3。

    新的离散方法为:在相差大于1的数间加一个数,例如在上面1 4 6 10中间加5(算法中实际上1,4之间,6,10之间都新增了数的)

    X[ 1 ] = 1, X[ 2 ] = 4, X[ 3 ] = 5, X[ 4 ] = 6, X[ 5 ] = 10

    这样之后,第一次是1~5被染成1;第二次1~2被染成2;第三次4~5被染成3

    最终,1~2为2,3为1,4~5为3,于是输出正确结果3。

    #include <iostream>
    #include <string>
    #include <cstring>
    #include <cstdlib>
    #include <cstdio>
    #include <cmath>
    #include <algorithm>
    #include <stack>
    #include <queue>
    #include <cctype>
    #include <vector>
    #include <iterator>
    #include <set>
    #include <map>
    #include <sstream>
    using namespace std;
    
    #define mem(a,b) memset(a,b,sizeof(a))
    #define pf printf
    #define sf scanf
    #define spf sprintf
    #define pb push_back
    #define debug printf("!
    ")
    #define MAXN 10000 + 5
    #define MAX(a,b) a>b?a:b
    #define blank pf("
    ")
    #define LL long long
    #define ALL(x) x.begin(),x.end()
    #define INS(x) inserter(x,x.begin())
    #define pqueue priority_queue
    #define INF 0x3f3f3f3f
    
    int n,m;
    
    int a[MAXN<<4],col[MAXN<<4],ans;
    
    int s1[MAXN],s2[MAXN];
    
    bool hh[MAXN];
    
    
    void PushDown(int rt)
    {
        if(col[rt] != -1)
        {
            col[rt<<1] = col[rt<<1|1] = col[rt];
            col[rt] = -1;
        }
    }
    
    void update(int val,int L,int R,int l,int r,int rt)
    {
        if(L <= l && r <= R)
        {
            col[rt] = val;
            return;
        }
        PushDown(rt);
        int mid = (l + r)>>1;
        if (L <= mid)
        {
            update(val,L,R,l,mid,rt<<1);
        }
        if(R > mid)
        {
            update(val,L,R,mid+1,r,rt<<1|1);
        }
    }
    
    void query(int l,int r,int rt)
    {
        if(l==r)
        {
            if(!hh[col[rt]])
            {
                ans++;
                hh[col[rt]] = true;
            }
            return;
        }
        PushDown(rt);
        int mid = (l + r)>>1;
        query(l,mid,rt<<1);
        query(mid+1,r,rt<<1|1);
    }
    
    int BSearch(int lo, int hi, int v)
    {
        int mid;
        while (lo <= hi)
        {
            mid = (lo + hi) >> 1;
            if (a[mid] == v) return mid;
            else if (a[mid] > v)  hi = mid - 1;
            else lo = mid + 1;
        }
        return -1;
    }
    
    
    int main()
    {
        int t,i,kase=1;
        sf("%d",&t);
        while(t--)
        {
            mem(col,-1);
            mem(a,0);
            mem(hh,false);
            sf("%d",&n);
            int cnt = 0,len = 1;
            for(i=1;i<=n;i++)//????
            {
                sf("%d %d",&s1[i],&s2[i]);
                a[++cnt] = s1[i];
                a[++cnt] = s2[i];
            }
            sort(a+1,a+1+cnt);
    
            for(i=2;i<=cnt;i++)//??
            {
                if(a[i]!=a[i-1]) a[++len] = a[i];
            }
    
            for(i=len;i>1;i--)//?????
            {
                if(a[i]-a[i-1]>1) a[++len] = a[i]-1;
            }
            sort(a+1,a+1+len);
    
            for(i=1;i<=n;i++)
            {
                int l = BSearch(1,len,s1[i]);
                int r = BSearch(1,len,s2[i]);
                update(i,l,r,1,len,1);
            }
            ans = 0;
            query(1,len,1);
            pf("%d
    ",ans);
        }
        return 0;
    }
  • 相关阅读:
    阿里图标库引用简介---20200608更新
    2.10排序算法
    2.9Node节点的学习
    2.8DOM节点的学习
    2.5数组
    2.6对象和函数
    2.7变量、内存、Math和Date的初级认识
    css优先级问题
    事件委托(事件代理)初认识
    静态页面学习随笔(1)-如何正确布局大体结构
  • 原文地址:https://www.cnblogs.com/qlky/p/5716796.html
Copyright © 2020-2023  润新知