zoukankan      html  css  js  c++  java
  • 线段树

    作为一个初学者,在这里就当是记一下笔记了

    线段树是一种RMQ(区间最大值)与树状数组(区间求和)的结合,用到了分治思想

    时间复杂度为nlogn,与树状数组和RMQ一样,但是有一个比较大的常数

    多用于数列

    多用于线段

    用完全二叉数的储存方式储存

    可以用结构体,也可以只用数组储存,差不多

    线段树的功能

    一、单点操作

    单点修改,区间求和,区间求最值

    二、区间操作

    成段操作,区间求和,区间求最值

    三、区间合并

    四、扫描线

    后两个有时间再上,我还没学完啦

    build

    #define lson l,m,rt<<1
    #define rson m+1,r,rt<<1|1
    void build(int l,int r,int rt)
    {
        if(l==r)
        {
            scanf("%d",&sum[rt]);
            return ;
        }
        int m=(l+r)>>1;
        build(lson);
        build(rson);
        PushUp(rt);
    }
    View Code

    单点修改或更新

    void update(int p,int add,int l,int r,int rt){
        if(l == r){
            sum[rt]+= add;
            return;
        }
        int m =(l + r)>>1;
        if(p <= m) update(p , add , lson);
        else update(p , add , rson);
        PushUP(rt);
    }
    View Code

    区间查询

    int query(int L,int R,int l,int r,int rt){
        if(L <= l && r <= R){
            return sum[rt];
        }
        int m =(l + r)>>1;
        int ret =0;
        if(L <= m) ret += query(L , R , lson);
        if(R > m) ret += query(L , R , rson);
        return ret;
    }
    View Code

    这个不大难,

    单点更新习题:
    hdu1166 敌兵布阵
    hdu1754 I Hate It
    hdu1394 Minimum Inversion Number
    hdu2795 Billboard
    poj2828 Buy Tickets

    二、区间更新
    需要用到延迟标记(或者说懒惰标记),简单来说就是每次更新的时候不要更新
    到底,用延迟标记使得更新延迟到下次需要更新or询问到的时候。
    具体操作为:
    延迟标记:每个节点新增加一个标记,记录这个节点是否进行了某种修改
    (这种修改操作会影响其子节点),对于任意区间的修改,我们先按照区间查
    询的方式将其划分成线段树中的节点,然后修改这些节点的信息,并给这些
    节点标记上代表这种修改操作的标记。在修改和查询的时候,如果我们到了
    一个节点p,并且决定考虑其子节点,那么我们就要看节点p是否被标记,如
    果有,就要按照标记修改其子节点的信息,并且给子节点都标上相同的标
    记,同时消掉节点p的标记。

    一道坎,会了就不难了

    延迟标记PushDown

    void PushDown(int rt,int m)
    {
        if(add[rt])
           {
            add[rt<<1]+= add[rt];
            add[rt<<1|1]+= add[rt];
            sum[rt<<1]+= add[rt]*(m -(m >>1));
            sum[rt<<1|1]+= add[rt]*(m >>1);
            add[rt]=0;
        }
    }
    View Code

    区间修改或更新

    void update(int L,int R,int c,int l,int r,int rt){
        if(L <= l && r <= R){
            add[rt]+= c;
            sum[rt]+=(LL)c *(r - l +1);
            return;
        }
        PushDown(rt , r - l +1);
        int m =(l + r)>>1;
        if(L <= m) update(L , R , c , lson);
        if(m < R) update(L , R , c , rson);
        PushUp(rt);
    }
    View Code

    区间查询

    LL query(int L,int R,int l,int r,int rt){
        if(L <= l && r <= R){
            return sum[rt];
        }
        PushDown(rt , r - l +1);
        int m =(l + r)>>1;
        LL ret =0;
        if(L <= m) ret += query(L , R , lson);
        if(m < R) ret += query(L , R , rson);
        return ret;
    }
    View Code

    区间更新习题:
    hdu1698 Just a Hook
    poj3468 A Simple Problem with Integers
    poj2528 Mayor’s posters
    poj3225 Help with Intervals (较难)
    Bzoj1798 Ahoi2009行星序列

    区间合并

    求连续最长序列

    这类题目会询问区间中满足条件的连续最长区间,所以PushUp的时候需要对左右儿子的区间进行合并

    void PushUp(int rt,int m){
        lsum[rt]= lsum[rt<<1];
        rsum[rt]= rsum[rt<<1|1];
        if(lsum[rt]== m -(m >>1)) lsum[rt]+= lsum[rt<<1|1];
        if(rsum[rt]==(m >>1)) rsum[rt]+= rsum[rt<<1];
        msum[rt]= max(lsum[rt<<1|1]+ rsum[rt<<1] , max(msum[rt<<1] , msum[rt<<1|1]));
    }
    View Code

    查询

    int query(int w,int l,int r,int rt)
    {
        if(l == r)return l;
        PushDown(rt , r - l +1);
        int m =(l + r)>>1;
        if(msum[rt<<1]>= w)return query(w , lson);
        elseif(rsum[rt<<1]+ lsum[rt<<1|1]>= w)return m - rsum[rt<<1]+1;
        return query(w , rson);
    }
    View Code

    剩下的自己调吧

    扫描线

    据说是码农题,所以,我也没怎么做

    用来求面积并或者是周长并

    hdu1542 Atlantis

    #include <cstdio>
    #include <cstring>
    #include <cctype>
    #include <algorithm>
    usingnamespace std;
    #define lson l , m , rt << 1
    #define rson m + 1 , r , rt << 1 | 1
     
    constint maxn =2222;
    int cnt[maxn <<2];
    double sum[maxn <<2];
    double X[maxn];
    struct Seg {
        double h , l , r;
        int s;
        Seg(){}
        Seg(double a,double b,double c,int d): l(a) , r(b) , h(c) , s(d){}
        bool operator <(const Seg &cmp)const{
            return h < cmp.h;
        }
    }ss[maxn];
    void PushUp(int rt,int l,int r){
        if(cnt[rt]) sum[rt]= X[r+1]- X[l];
        elseif(l == r) sum[rt]=0;
        else sum[rt]= sum[rt<<1]+ sum[rt<<1|1];
    }
    void update(int L,int R,int c,int l,int r,int rt){
        if(L <= l && r <= R){
            cnt[rt]+= c;
            PushUp(rt , l , r);
            return;
        }
        int m =(l + r)>>1;
        if(L <= m) update(L , R , c , lson);
        if(m < R) update(L , R , c , rson);
        PushUp(rt , l , r);
    }
    int Bin(double key,int n,double X[]){
        int l =0 , r = n -1;
        while(l <= r){
            int m =(l + r)>>1;
            if(X[m]== key)return m;
            if(X[m]< key) l = m +1;
            else r = m -1;
        }
        return-1;
    }
    int main(){
        int n , cas =1;
        while(~scanf("%d",&n)&& n){
            int m =0;
            while(n --){
                double a , b , c , d;
                scanf("%lf%lf%lf%lf",&a,&b,&c,&d);
                X[m]= a;
                ss[m++]= Seg(a , c , b , 1);
                X[m]= c;
                ss[m++]= Seg(a , c , d , -1);
            }
            sort(X , X + m);
            sort(ss , ss + m);
            int k =1;
            for(int i =1; i < m ; i ++){
                if(X[i]!= X[i-1]) X[k++]= X[i];
            }
            memset(cnt , 0 , sizeof(cnt));
            memset(sum , 0 , sizeof(sum));
            double ret =0;
            for(int i =0; i < m -1; i ++){
                int l = Bin(ss[i].l , k , X);
                int r = Bin(ss[i].r , k , X)-1;
                if(l <= r) update(l , r , ss[i].s , 0 , k -1, 1);
                ret += sum[1]*(ss[i+1].h- ss[i].h);
            }
            printf("Test case #%d
    Total explored area: %.2lf
    
    ",cas++ , ret);
        }
        return0;
    }
    View Code

    就到这里吧,周长并比面积并难多了...

  • 相关阅读:
    sfs2x 连接 mongodb
    java websocket
    webstorm 4.0 注册码
    解决 sfs2 admin tool 找不到扩展
    window 注册表五大类
    opengl 学习第二日
    java google Protobuf
    扩展 java sencha touch PhonegapPlugin
    sencha touch2 kryonet socket phonegap 通信 作者:围城
    sencha touch2 layout 笔记
  • 原文地址:https://www.cnblogs.com/Winniechen/p/6946620.html
Copyright © 2011-2022 走看看