线段树

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

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

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

原文地址:https://www.cnblogs.com/Winniechen/p/6946620.html