线段树

线段树的一般结构:

图片转自:https://www.cnblogs.com/TheRoadToTheGold/p/6254255.html

1.pushup

把子节点的信息向上传递

2.pushdown

把信息向子节点传递

pushdown是在查和改到目标区域才向下传递,也就是这个标记很懒

用他他才向下传递,不需要每次都传递到叶子结点,避免重复多次修改叶子结点的懒惰标记,很多时候在上面就已经结束了

这就是懒惰标记

注意事项:

线段树一般要开4倍空间.

多组输入要清空懒惰标记和一些特殊标记

注意区分(a,b)和(l,r),不要混淆

线段树单点修改区间求和:

#include
#define ls l, m, rt<<1
#define rs m+1,r,rt<<1 |1
#define ll long long
#define MAXN 100005
using namespace std;
ll sum[MAXN<<2];

void pushup(int rt)
{
    sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}

void build(int l,int r,int rt)
{
    if(l==r)
    {
        scanf("%lld",&sum[rt]);
        return ;
    }
    int m=(l+r)>>1;
    build(ls);
    build(rs);
    pushup(rt);
}

void update(int d,int v,int l,int r,int rt)
{
    if(l==r)
    {
        sum[rt]+=v;
        return ;
    }
    int m=(l+r)>>1;
    if(d<=m)//只要修改左区间
        update(d,v,ls);
    else//只要修改右区间
        update(d,v,rs);
    pushup(rt);
}

ll query(int a,int b,int l,int r,int rt)
{
    if(a<=l&&r<=b)
    {
        return sum[rt];
    }
    int m=(l+r)>>1;
    ll res=0;
    if(a<=m)//需要查询左区间
        res+=query(a,b,ls);
    if(b>m)//需要查询右区间
        res+=query(a,b,rs);
    return res;
}

int main()
{
    int n,m,x,d,v,a,b,t;
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        build(1,n,1);
        while(m--)
        {
            scanf("%d",&x);
            if(x==1)
            {
                scanf("%d%d",&d,&v);
                update(d,v,1,n,1);
            }
            else{
                scanf("%d%d",&a,&b);
                printf("%lld\n",query(a,b,1,n,1));
            }
        }
    }
    return 0;
}

单点修改,区间求极值:

#include
#define ll long long
#define MAXN 200005
#define ls l,m,rt<<1
#define rs m+1,r,rt<<1|1
using namespace std;
int maxs[MAXN<<2],mins[MAXN<<2];
int x;

void maxmin(int rt)
{
    maxs[rt]=max(maxs[rt<<1],maxs[rt<<1|1]);
    mins[rt]=min(mins[rt<<1],mins[rt<<1|1]);
}

void build(int l,int r,int rt)
{
    if(l==r)
    {
        scanf("%d",&x);
        maxs[rt]=mins[rt]=x;
        return ;
    }
    int m=(l+r)>>1;
    build(ls);
    build(rs);
    maxmin(rt);
}

void update(int d,int v,int l,int r,int rt)
{
    if(l==r)
    {
        maxs[rt]+=v;
        return ;
    }
    int m=(l+r)>>1;
    if(d<=m)
        update(d,v,ls);
    else
        update(d,v,rs);
    maxmin(rt);
}

int query(int a,int b,int l,int r,int rt)
{
    if(a<=l&&r<=b)
    {
        return maxs[rt];
    }
    int m=(l+r)>>1;
    int res=0;
    if(a<=m)
        res=max(res,query(a,b,ls));
    if(b>m)
        res=max(res,query(a,b,rs));
    return res;
}

int query2(int a,int b,int l,int r,int rt)
{
    if(a<=l&&r<=b)
    {
        return mins[rt];
    }
    int m=(l+r)>>1;
    int res=1e9;
    if(a<=m)
        res=min(res,query2(a,b,ls));
    if(b>m)
        res=min(res,query2(a,b,rs));
    return res;
}

int main()
{
    int n,m,d,a,b,v;
    scanf("%d%d",&n,&m);
    build(1,n,1);
    while(m--)
    {
        scanf("%d",&x);
        if(x==1)
        {
            scanf("%d%d",&d,&v);
            update(d,v,1,n,1);
        }
        else if(x==2){
            scanf("%d%d",&a,&b);
            printf("%d\n",query(a,b,1,n,1));
        }
        else{
            scanf("%d%d",&a,&b);
            printf("%d\n",query2(a,b,1,n,1));
        }
    }
    return 0;
}

区间修改(修改为定值)区间求和:

#include
#define ls l,m,rt<<1
#define rs m+1,r,rt<<1|1
#define ll long long
#define MAXN 100005
using namespace std;
ll sum[MAXN<<2],add[MAXN<<2];

void pushdown(int l,int r,int rt)//懒惰标记,向下推区间
{
    if(add[rt])
    {
        add[rt<<1]=add[rt];
        add[rt<<1|1]=add[rt];
        int m=(l+r)>>1;
        sum[rt<<1]=add[rt]*(m-l+1);
        sum[rt<<1|1]=add[rt]*(r-(m+1)+1);
        add[rt]=0;
    }
}

void pushup(int rt)
{
    sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}

void build(int l,int r,int rt)
{
    if(l==r)
    {
        sum[rt]=1;
        return ;
    }
    int m=(l+r)>>1;
    build(ls);
    build(rs);
    pushup(rt);
}

void update(int a,int b,int v,int l,int r,int rt)
{
    if(a<=l&&r<=b)
    {
        sum[rt]=(r-l+1)*v;
        add[rt]=v;
        return ;
    }
    int m=(l+r)>>1;
    pushdown(l,r,rt);
    if(a<=m)
        update(a,b,v,ls);
    if(b>m)
        update(a,b,v,rs);
    pushup(rt);
}

ll query(int a,int b,int l,int r,int rt)
{
    if(a<=l&&r<=b)
    {
        return sum[rt];
    }
    int m=(l+r)>>1;
    pushdown(l,r,rt);
    ll res=0;
    if(a<=m)
        res+=query(a,b,ls);
    if(b>m)
        res+=query(a,b,rs);
    return res;
}

int main()
{
    int n,m,a,b,v,x,cas=1,t;
    scanf("%d%d",&n,&m);
    build(1,n,1);
    while(m--)
    {
        scanf("%d",&x);
        if(x==1)
        {
            scanf("%d%d%d",&a,&b,&v);
            update(a,b,v,1,n,1);
        }
        else{
            scanf("%d%d",&a,&b);
            printf("%lld\n",query(a,b,1,n,1));
        }
    }
    return 0;
}

POJ - 3468

区间修改(加,减),区间查询:

#include
#define ll long long
#define maxn 100007
#define ls l,m,rt<<1
#define lr m+1,r,rt<<1|1
ll sum[maxn<<2]={0},add[maxn<<2]={0};
int a[maxn];

void pushup(int rt)
{
    sum[rt]=sum[rt<<1]+sum[rt<<1|1];
}

void pushdown(int l,int r,int rt)//下推标记函数
{
    if(add[rt])
    {
        add[rt<<1]+=add[rt];
        add[rt<<1|1]+=add[rt];
        int m=(l+r)>>1;
        sum[rt<<1]+=add[rt]*(m-l+1);
        sum[rt<<1|1]+=add[rt]*(r-(m+1)+1);
        add[rt]=0;
    }
}

void build(int l,int r,int rt)//建立树
{
    if(l==r)
    {
        sum[rt]=a[l];
        return ;
    }
    int m=(l+r)>>1;
    build(l,m,rt<<1);
    build(m+1,r,rt<<1|1);
    pushup(rt);
}

void update2(int L,int R,int C,int l,int r,int rt)//区间修改
{
    if(L<=l&&r<=R)
    {
        sum[rt]+=C*(r-l+1);
        add[rt]+=C;
        return ;
    }
    int m=(l+r)>>1;
    pushdown(l,r,rt);
    if(L<=m) update2(L,R,C,l,m,rt<<1);
    if(R>m) update2(L,R,C,m+1,r,rt<<1|1);
    pushup(rt);
}

long long query(int L,int R,int l,int r,int rt)//区间查询
{
    if(L<=l&&r<=R)
        return sum[rt];
    int m=(l+r)>>1;
    pushdown(l,r,rt);
    long long ans=0;
    if(L<=m) ans+=query(L,R,l,m,rt<<1);
    if(R>m) ans+=query(L,R,m+1,r,rt<<1|1);
    return ans;
}

int main()
{
    int n,m,x,y,w;
    scanf("%d%d",&n,&m);
    for(int i=1;i<=n;i++)
        scanf("%d",&a[i]);
    build(1,n,1);
    char str[5];
    for(int i=0;i

区间修改,区间极值

#include
#define MAXN 200005
#define ls l,m,rt<<1
#define rs m+1,r,rt<<1|1
using namespace std;
int maxs[MAXN<<2];
int add[MAXN<<2];

void push_up(int rt)
{
    maxs[rt]=max(maxs[rt<<1],maxs[rt<<1|1]);
}

void pushdown(int l,int r,int rt)
{
    if(add[rt]){
        add[rt<<1]+=add[rt];
        add[rt<<1|1]+=add[rt];
        maxs[rt<<1]+=add[rt];
        maxs[rt<<1|1]+=add[rt];
        add[rt]=0;
    }
}

void build(int l,int r,int rt)
{
    if(l==r){
        maxs[rt]=0;
        return ;
    }
    int m=(l+r)>>1;
    build(ls);
    build(rs);
}

void update(int a,int b,int c,int l,int r,int rt)
{
    if(a<=l&&r<=b)
    {
        add[rt]+=c;
        maxs[rt]+=c;
        return ;
    }
    int m=(l+r)>>1;
    pushdown(l,r,rt);
    if(a<=m)
        update(a,b,c,ls);
    if(b>m)
        update(a,b,c,rs);
    push_up(rt);
}

int query(int a,int b,int l,int r,int rt)
{
    if(a<=l&&r<=b)
    {
        return maxs[rt];
    }
    int m=(l+r)>>1;
    pushdown(l,r,rt);
    int ans=0;
    if(a<=m)
        ans=max(ans,query(a,b,ls));
    if(b>m)
        ans=max(ans,query(a,b,rs));
    return ans;
}

int main()
{
    int n,m,l,r,x,v;
    scanf("%d%d",&n,&m);
    build(1,n,1);
    while(m--)
    {
        scanf("%d",&x);
        if(x==1){//修改
            scanf("%d%d%d",&l,&r,&v);
            update(l,r,1,v,n,1);
        }
        else{//查询
            scanf("%d%d",&l,&r);
            printf("%d\n",query(l,r,1,n,1));
        }
    }
    return 0;
}

区间连续最长上升子序列长度,单点修改,区间查询

区间合

ac:

#include
#define ls l,m,rt<<1
#define rs m+1,r,rt<<1|1
#define ll long long
#define MAXN 200005
using namespace std;
int lsum[MAXN<<2];
int rsum[MAXN<<2];
int msum[MAXN<<2];
int num[MAXN];
int x;

void pushup(int l,int r,int rt)
{
    int m=(l+r)>>1;
    lsum[rt]=lsum[rt<<1];
    rsum[rt]=rsum[rt<<1|1];
    msum[rt]=max(msum[rt<<1],msum[rt<<1|1]);
    if(num[m]>1;
    build(ls);
    build(rs);
    pushup(l,r,rt);
}

void update(int d,int v,int l,int r,int rt)
{
    if(l==r)
    {
        num[d]=v;
        return ;
    }
    int m=(l+r)>>1;
    if(m>=d)
        update(d,v,ls);
    else
        update(d,v,rs);
    pushup(l,r,rt);
}

int query(int a,int b,int l,int r,int rt)
{
    if(a<=l&&r<=b)
    {
        return msum[rt];
    }
    int res=0;
    int m=(l+r)>>1;
    if(m>=a)
        res=max(res,query(a,b,ls));
    if(b>m)
        res=max(res,query(a,b,rs));
    if(num[m]

D - Welfare State

解析:

长n的数组,q次操作

最后单点查询n次

q:1 x v  将x修改为v

q: 2 x    修改小于x的所以a[i]为x

写的时候,没有注意pushdown的add[rt]不一定比add[rt<<1]和add[rt<<1|1]大

因为每次pushdown不一定进行到底部,要判断是否变

ac:

#include
#define ls l,m,rt<<1
#define rs m+1,r,rt<<1|1
#define ll long long
#define MAXN 400005
using namespace std;
ll sum[MAXN<<2],add[MAXN<<2];
ll aa[MAXN<<2];

void pushdown(ll l,ll r,ll rt)//懒惰标记,向下推区间
{
    if(add[rt])
    {
        add[rt<<1]=max(add[rt],add[rt<<1]);//注意这里,pushdown并不是每次都自底到下,所以这里也要判断是否变小
        add[rt<<1|1]=max(add[rt],add[rt<<1|1]);
        ll m=(l+r)>>1;

        if(m-l+1<=1)
            sum[rt<<1]=max(add[rt],sum[rt<<1]);
        else sum[rt<<1]=add[rt]*(m-l+1);

        if((r-(m+1)+1)<=1)
            sum[rt<<1|1]=max(sum[rt<<1|1],add[rt]);
        else sum[rt<<1|1]=add[rt]*(r-(m+1)+1);

        add[rt]=0;
    }
}

void build(ll l,ll r,ll rt)
{
    if(l==r)
    {
        sum[rt]=aa[l];
        return ;
    }
    ll m=(l+r)>>1;
    build(ls);
    build(rs);
}

void update(ll a,ll b,ll v,ll l,ll r,ll rt)
{
    if(a<=l&&r<=b)
    {
        sum[rt]=max(v,sum[rt]);
        add[rt]=max(v,add[rt]);
        return ;
    }
}

void update2(ll a,ll b,ll v,ll l,ll r,ll rt)
{
    if(a<=l&&r<=b)
    {
        sum[rt]=v;
        return ;
    }
    ll m=(l+r)>>1;
    pushdown(l,r,rt);
    if(a<=m)
        update2(a,b,v,ls);
    if(b>m)
        update2(a,b,v,rs);
}

ll query(ll a,ll b,ll l,ll r,ll rt)
{
    if(a<=l&&r<=b)
    {
        return sum[rt];
    }
    ll m=(l+r)>>1;
    pushdown(l,r,rt);
    ll res=0;
    if(a<=m)
        res+=query(a,b,ls);
    if(b>m)
        res+=query(a,b,rs);
    return res;
}

int main()
{
    ll n,m,a,b,v,x,cas=1,t;
    scanf("%lld",&n);
    for(ll i=1;i<=n;i++)
        scanf("%lld",&aa[i]);
    build(1,n,1);
    scanf("%lld",&m);
    while(m--)
    {
        scanf("%lld",&x);
        if(x==1)
        {
            scanf("%lld%lld",&a,&v);
            update2(a,a,v,1,n,1);
        }
        else{
            scanf("%lld",&v);
            update(1,n,v,1,n,1);
        }
    }
    for(ll i=1;i<=n;i++)
        printf("%lld ",query(i,i,1,n,1));
    return 0;
}

矩形面积并:https://vjudge.net/problem/POJ-1151

解析:

扫描线+线段树

将上边,下边离散化后排序,按l排序,保存高度,和如果是下边标记1,上边标记-1

然后从下往上一层一层的计算

代码:

#include
#include
#include
#include
#include
#include
#define MAXN 505
using namespace std;
int add[MAXN<<2];
double x[MAXN<<2],sum[MAXN<<2];

void init()
{
    memset(add,0,sizeof(add));
    memset(sum,0,sizeof(sum));
    memset(x,0,sizeof(x));
}

struct node
{
    double l,r,h;
    int d;
    friend bool operator <(node a,node b)
    {
        return a.h>1;
    if(L<=m)
        update(L,R,c,l,m,rt<<1);
    if(R>m)
        update(L,R,c,m+1,r,rt<<1|1);
    pushup(l,r,rt);
}

int main()
{
    int t,n,cas=1;
    double a,b,c,d;
    while(scanf("%d",&n)&&n)
    {
        init();
        int num=0,k=1;
        for(int i=1;i<=n;i++)
        {
            scanf("%lf%lf%lf%lf",&a,&b,&c,&d);
            x[++num]=a;
            seg[num]=node{a,c,b,1};
            x[++num]=c;
            seg[num]=node{a,c,d,-1};
        }
        sort(x+1,x+num+1);
        sort(seg+1,seg+num+1);
        k=unique(x+1,x+num+1)-x-1;
        double ans=0;
        for(int i=1;i<=num-1;i++)//从下往上一层一层计算
        {
            int l=lower_bound(x+1,x+k+1,seg[i].l)-x;
            int r=lower_bound(x+1,x+k+1,seg[i].r)-x-1;
            update(l,r,seg[i].d,1,k,1);
            ans+=sum[1]*(seg[i+1].h-seg[i].h);
        }
        printf("Test case #%d\nTotal explored area: %.2f\n\n",cas++,ans);
    }
    return 0;
}
/*
2
10 10 20 20
15 15 25 25.5
*/

 

你可能感兴趣的:(数据结构)