• 线段树


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

    线段树是一种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

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

  • 相关阅读:
    Junit初级编码(一)第一个Junit测试程序
    关于健身与健美
    spring入门(一)
    HTTP状态301、404、200、304分别表示什么意思
    预处理prepareStatement是怎么防止sql注入漏洞的?
    详解SESSION与COOKIE的区别
    Form表单中method为get和post的区别
    tomcat服务器配置及使用
    ubuntu 14.04 安装mysql server初级教程
    《电路学习第三天》 之 线性稳压电源的设计
  • 原文地址:https://www.cnblogs.com/Winniechen/p/6946620.html
Copyright © 2020-2023  润新知