线段树入门题

先上板子(不过线段树的使用经常会有要修改的地方,需要自己能够独立的敲出来,这个和图论的那些板子就不一样了)

单点更新,区间和查询

/********************************
线段树的单点更新和区间和查询 
********************************/
#include <bits/stdc++.h>
using namespace std;
#define ls l,m,rt<<1
#define rs m+1,r,rt<<1|1

const int MAXN=50005;
int tree[MAXN<<2];

void push_up(int rt)
{
    tree[rt]=tree[rt<<1]+tree[rt<<1|1];
}

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

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

int query(int L, int R, int l, int r, int rt)
{   
    if(L<=l && r<=R) return tree[rt];
    int m=(l+r)>>1, ret=0;
    if(L<=m) ret+=query(L, R, ls);
    if(R> m) ret+=query(L, R, rs);
    return ret;
}

int main()
{
    return 0;
}
View Code

单点更新,区间最值查询

/******************************* 
线段树的单点更新和区间最值查询 
*******************************/ 
#include <bits/stdc++.h>
using namespace std;
#define ls l,m,rt<<1
#define rs m+1,r,rt<<1|1

const int MAXN=50005;
int tree[MAXN<<2];

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

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

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

int query(int L, int R, int l, int r, int rt)
{   
    if(L<=l && r<=R) return tree[rt];
    int m=(l+r)>>1, ret=-1;
    if(L<=m) ret=max(ret, query(L, R, ls));
    if(R> m) ret=max(ret, query(L, R, rs));
    return ret;
}

int main()
{
    return 0;
}
View Code

区间更新,区间和查询

注意:每次往下层query或者update的之前都需要延迟标记下放

/***************************
线段树的区间更新和区间和查询 
***************************/
#include <bits/stdc++.h> 
using namespace std;
#define ll long long
#define ls l,m,rt<<1
#define rs m+1,r,rt<<1|1

const int MAXN=50005; 
ll tree[MAXN<<2], add[MAXN<<2];

void push_up(int rt)
{
    tree[rt]=tree[rt<<1]+tree[rt<<1|1];
}

void push_down(int rt, int m)//m是当前节点区间的范围的长度 
{         
    if(add[rt])
    {
        add[rt<<1]+=add[rt]; 
        add[rt<<1|1]+=add[rt];
        tree[rt<<1]+=(m-(m>>1))*add[rt];
        tree[rt<<1|1]+=(m>>1)*add[rt];
        add[rt]=0;
    }
}

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

void update(int L, int R, int c, int l, int r, int rt)
{    
    if(L<=l && r<=R) 
    {
        add[rt]+=c; 
        tree[rt]+=1ll*c*(r-l+1); 
        return ;
    }
    push_down(rt, r-l+1);
    int m=(l+r)>>1;
    if(L<=m) update(L, R, c, ls);
    if(R> m) update(L, R, c, rs);
    push_up(rt);
}

ll query(int L,int R,int l,int r,int rt)
{  
    if(L<=l && r<=R) return tree[rt];
    push_down(rt, r-l+1);
    int m=(l+r)>>1;
    ll ret=0;
    if(L<=m) ret+=query(L, R, ls);
    if(R> m) ret+=query(L, R, rs);
    return ret;
}

int main()
{
    return 0;
}
View Code

POJ 3468(模板题,验板)

/***************************
线段树的区间更新和区间和查询 
***************************/
//#include <bits/stdc++.h> 
#include <cstdio>
using namespace std;
#define ll long long
#define ls l,m,rt<<1
#define rs m+1,r,rt<<1|1

const int MAXN=1e5+5; 
ll tree[MAXN<<2], add[MAXN<<2];

void push_up(int rt)
{
    tree[rt]=tree[rt<<1]+tree[rt<<1|1];
}

void push_down(int rt, int m)//m是当前节点区间的范围的长度 
{         
    if(add[rt])
    {
        add[rt<<1]+=add[rt]; 
        add[rt<<1|1]+=add[rt];
        tree[rt<<1]+=(m-(m>>1))*add[rt];
        tree[rt<<1|1]+=(m>>1)*add[rt];
        add[rt]=0;
    }
}

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

void update(int L, int R, int c, int l, int r, int rt)
{    
    if(L<=l && r<=R) 
    {
        add[rt]+=c; 
        tree[rt]+=1ll*c*(r-l+1); 
        return ;
    }
    push_down(rt, r-l+1);
    int m=(l+r)>>1;
    if(L<=m) update(L, R, c, ls);
    if(R> m) update(L, R, c, rs);
    push_up(rt);
}

ll query(int L, int R, int l, int r, int rt)
{  
    if(L<=l && r<=R) return tree[rt];
    push_down(rt, r-l+1);
    int m=(l+r)>>1;
    ll ret=0;
    if(L<=m) ret+=query(L, R, ls);
    if(R> m) ret+=query(L, R, rs);
    return ret;
}

int main()
{
    int n, q;
    scanf("%d%d", &n, &q);
    build(1, n, 1);
    while(q--)
    {
        char cmd[10];
        int a, b, c;
        scanf("%s%d%d", cmd, &a, &b); //scanf("%c")是会读取会回车的... 
        if(cmd[0]=='Q') printf("%lld
", query(a, b, 1, n, 1));
        else {
            scanf("%d", &c);
            update(a, b, c, 1, n, 1);
        }
    }
    return 0;
}
View Code

HDU 1394(求逆序对的个数)

注意:如果给出的数据不是离散且连续的话,需要先离散化。

#include <bits/stdc++.h>
using namespace std;
#define _for(i,a,b) for(int i=(a); i< (b); i++)
#define _rep(i,a,b) for(int i=(a); i<=(b); i++)
#define ls l,m,rt<<1
#define rs m+1,r,rt<<1|1

const int MAXN=50005;
int tree[MAXN<<2];

void push_up(int rt)
{
    tree[rt]=tree[rt<<1]+tree[rt<<1|1];
}

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

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

int query(int L, int R, int l, int r, int rt)
{   
    if(L<=l && r<=R) return tree[rt];
    int m=(l+r)>>1, ret=0;
    if(L<=m) ret+=query(L, R, ls);
    if(R> m) ret+=query(L, R, rs);
    return ret;
}
int a[MAXN];

int main()
{
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    int n;
    while(cin>>n)
    {
        int ans=0, sum=0;
        build(0, n-1, 1);
        _for(i, 0, n)
        {
            cin>>a[i];
            sum+=query(a[i]+1, n-1,  0, n-1, 1);
            update(a[i], 1, 0, n-1, 1);
        }
        ans=sum;
        _for(i, 0, n)
        {
            sum+=(n-1-a[i])-a[i];
            ans=min(ans, sum);
        }
        cout<<ans<<endl;    
    }
    return 0;
}
View Code

HDU 2795

题意:h*w的木板,放进一些1*L的物品(横着放)优先往上层放,求层数,不能放的话-1

题解:线段树的底部对应h,值对应该层剩余的最大空间,向上更新最大值,每一次查询优先往左子树走,查询后进行更改,并向上更新。

   还有一点,这个h的范围很大,但是k的范围较小,h最多只要k层即可。

   备注:数据量很大输出很多,发现一个问题,cout时候<<endl 5100ms, <<" " 1600ms(比scanf还要快)

#include <bits/stdc++.h>
using namespace std;
#define ls l,m,rt<<1
#define rs m+1,r,rt<<1|1

const int MAXN=2e5+5;
int tree[MAXN<<2];

int h, w, k;
void push_up(int rt)
{
    tree[rt]=max(tree[rt<<1],tree[rt<<1|1]);
}

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

int query(int x, int l, int r, int rt)  //单点查询和更新 
{
    if(l==r){
        tree[rt]-=x;
        return l;
    }
    int m=l+r>>1;
    int ret=(tree[rt<<1]>=x? query(x, ls):query(x, rs)); //优先往左子树走 
    push_up(rt);
    return ret; 
}

int main()
{
    //freopen("in.txt", "r", stdin);
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    while(cin>>h>>w>>k)
    {
        if(h>k) h=k;
        build(1, h, 1);
        while(k--)
        {
            int x; cin>>x;
            if(tree[1]<x) cout<<"-1"<<"
";
            else cout<<query(x, 1, h, 1)<<"
";
        }
    }
    return 0;
}
View Code

POJ 2528 (离散化+线段树)

题意:按次序给你贴广告时的位置(左端点和右端点),求能看得见多少个广告牌。

题解:此题中l和r的范围很大,需要对点的坐标离散化(不改变数据的相对大小下(不影响结果),缩小数据的范围)。

   底层表示总区间,从后往前进行查询并覆盖,判断是否能看见。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define ls l,m,rt<<1
#define rs m+1,r,rt<<1|1

const int MAXN=2e4+5, MAXM=1e7+5;
int tree[MAXN<<2], pos[MAXN], pl[MAXN], pr[MAXN];
int hash[MAXM];
void push_up(int rt)
{
    tree[rt]=tree[rt<<1] && tree[rt<<1|1];
}

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

int query(int L, int R, int l, int r, int rt)  
{     //注意这个区间修改的过程中,因为这个只是0或者1,所以只用修改到完全包含该区间的位置即可(就是放延迟标记的地方即可)不用push_down 
    //往下查询的时候先判断是否被标记即可 (其实就是我们要查询的地方是否被延迟标记过了) 
    //同理,建树的时候,每一个节点都要归0(push_up加上去即可,或者直接赋值),都把我给调傻了....(其实感觉直接延迟标记成段更新反而没有这么多的细节Orz)  
    if(tree[rt]) return 0;
    if(L<=l && r<=R){
        tree[rt]=1;
        return 1;
    }
    int m=l+r>>1;
    /*
    int okl=1, okr=1;               WA
    if(L<=m)  okl=query(L, R, ls);
    if(R> m)  okr=query(L, R, rs);
    ok=okl||okr;
    */
    int ok, okl, okr;
    if(L>m) ok=query(L, R, rs);
    else if(R<=m) ok=query(L, R, ls);
    else 
    {
        okl=query(L, R, ls);
        okr=query(L, R, rs);
        ok=okl || okr;    
    } 
    push_up(rt);
    return ok;
}

void print(int l, int r, int rt) //debug
{
    printf("! l=%d, r=%d, is_ok=%d
", l, r, tree[rt]);
    if(l==r) return; 
    int m=l+r>>1;
    print(ls); print(rs);    
} 

int main()
{
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0); //注意调试的时候用printf的时候不要解除流绑定 
//    freopen("in.txt", "r", stdin);
    int T; 
    for(cin>>T; T--; )
    {
        int n; cin>>n; 
        for(int i=0; i<n; i++)
        {
            cin>>pl[i]>>pr[i];
            pos[2*i]=pl[i];
            pos[2*i+1]=pr[i];
        }
        sort(pos, pos+2*n);
        int cnt=unique(pos, pos+2*n)-pos;
        for(int i=0; i<cnt; i++)
            hash[pos[i]]=i;
        build(0, cnt-1, 1);
        int ans=0;
        for(int i=n-1; i>=0; i--)
        {
            if(query(hash[pl[i]], hash[pr[i]], 0, cnt-1, 1))
            {
                //printf("! i=%d,l=%d, r=%d
", i, hash[pl[i]], hash[pr[i]]);
                ans++;
                //print(0, cnt-1, 1);
                //printf("
");
            }
        }
        cout<<ans<<"
";
    }
    return 0;
}
View Code

HDU 1542 矩形面积并(线段树+离散化+扫描线)

题意:求多个矩形并后的总面积。

题解:扫描线,从下往上扫,cnt记录每一个区间的下位边和上位边的差值,sum记录每个区间下该条扫描线的有效覆盖长度。

   用线段树维护这两个值,push_up分几种情况,别漏了。

   数据是浮点数,离散化线段树的l,r和对应每一段区间的编号即可。

   备注:这题调了好久,1,sum在计算的时候出现了负数,发现ls和rs预定义的时候<<写反了(太sb了)。 2.用lower_bound查询会MLE(等我去查查)。

#include <bits/stdc++.h>
using namespace std;
#define ls l, m, rt<<1
#define rs m+1, r, rt<<1|1

const int MAXN=2e3+5;
double sum[MAXN<<2], X[MAXN];
int cnt[MAXN<<2];

struct Line{
    double l, r, h;
    int f; 
    Line() {}
    Line(double a, double b, double c, int d) : l(a), r(b), h(c), f(d) {}
    bool operator <(const Line &b) const {
        return h<b.h;
    }
}lines[MAXN];

void push_up(int rt, int l, int r)
{
    if(cnt[rt]) sum[rt]=X[r+1]-X[l];  //该区间被完全覆盖,注意cnt的范围一定是>=0的 
    else if(l==r) sum[rt]=0;          //该区间是叶子节点,且cnt==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;
        push_up(rt, l, r);
        return ;
    }
    int m=l+r>>1;
    if(L<=m) update(L, R, c, ls);
    if(R> m) update(L, R, c, rs);
    push_up(rt, l, r);
}

int bin(double val, int l, int r, double x[])
{
    while(l<=r)
    {
        int m=l+r>>1;
        if(x[m]==val) return m;
        else if(x[m]<val) l=m+1;
        else if(x[m]>val) r=m-1; 
    }
    return -1;
}

int main()
{
    //freopen("in.txt", "r", stdin);
    int n, kase=0; 
    while(cin>>n, n)
    {
        int m=0;
        for(int i=0; i<n; i++)
        {
            double x1, y1, x2, y2;
            cin>>x1>>y1>>x2>>y2;
            X[m]=x1; lines[m++]=Line(x1, x2, y1, 1);
            X[m]=x2; lines[m++]=Line(x1, x2, y2, -1);
        }
        sort(X, X+m);
        sort(lines, lines+m);
        int k=unique(X, X+m)-X;
        memset(sum, 0, sizeof(sum));
        memset(cnt, 0, sizeof(cnt)); //build
        double ans=0; 
        for(int i=0; i<m-1; i++)
        {
            //int l=lower_bound(X, X+m, lines[i].l)-X;
            //int r=lower_bound(X, X+m, lines[i].r)-X-1;  MLE 不明白为啥会这样 
            int l=bin(lines[i].l, 0, k-1, X);
            int r=bin(lines[i].r, 0, k-1, X)-1;
            if(l<=r) update(l, r, lines[i].f, 0, k-1, 1);
            ans+=sum[1]*(lines[i+1].h-lines[i].h);
        }
        printf("Test case #%d
Total explored area: %.2lf

",++kase, ans);
    }
    return 0;
} 
View Code

POJ 3667 区间合并

题意: 2个指令1 a:询问是不是有连续长度为a的空房间,输出最左边起始的位置,并占领该段区间  2 a b:将[a,a+b-1]的房间清空

题解:区间合并入门题。线段树维护左右子树,左边,中间,右边,和交叉的空闲区间的长度。

//#include <bits/stdc++.h>
#include <iostream>
#include <cstdio>
using namespace std;
#define ls l, m, rt<<1
#define rs m+1, r, rt<<1|1 

const int MAXN=5e4+5;
int cover[MAXN<<2]; 
int lsum[MAXN<<2], rsum[MAXN<<2], msum[MAXN<<2];

void push_up(int rt, int m)
{
    lsum[rt]=lsum[rt<<1]; rsum[rt]=rsum[rt<<1|1];
    if(lsum[rt]==m-m/2) lsum[rt]+=lsum[rt<<1|1]; //注意这里别写成了m-m>>1 
    if(rsum[rt]==m/2  ) rsum[rt]+=rsum[rt<<1];
    msum[rt]=max(rsum[rt<<1]+lsum[rt<<1|1], max(msum[rt<<1], msum[rt<<1|1]));  //分3种情况 
}

void push_down(int rt, int m)
{
    if(cover[rt]!=-1)
    {
        cover[rt<<1]=cover[rt<<1|1]=cover[rt];
        lsum[rt<<1]=rsum[rt<<1]=msum[rt<<1]= cover[rt]? 0:m-m/2;
        lsum[rt<<1|1]=rsum[rt<<1|1]=msum[rt<<1|1]= cover[rt]? 0:m/2;
        cover[rt]=-1;
    } 
}

void build(int l, int r, int rt)
{
    lsum[rt]=rsum[rt]=msum[rt]=r-l+1;
    cover[rt]=-1;    //归为-1,更新的时候会放0,或者1的延迟标记  
    if(l==r) return ;
    int m=l+r>>1;
    build(ls); build(rs);
}

int query(int a, int l, int r, int rt)
{
    if(l==r) return l;
    push_down(rt, r-l+1);
    int m=l+r>>1;
    if(msum[rt<<1]>=a) return query(a, ls); //优先向左走,三种情况,左中右 
    else if(rsum[rt<<1]+lsum[rt<<1|1]>=a) return m-rsum[rt<<1]+1;
    else return query(a, rs);
}

void update(int L, int R, int v, int l, int r, int rt)
{
    if(L<=l && r<=R) 
    {
        lsum[rt]=rsum[rt]=msum[rt]= v? 0:r-l+1;
        cover[rt]=v;
        return ;
    }
    push_down(rt, r-l+1);
    int m=l+r>>1;
    if(L<=m) update(L, R, v, ls);
    if(R >m) update(L, R, v, rs);
    push_up(rt, r-l+1);
}

void print(int l, int r, int rt)
{
    printf("! l=%d, r=%d, lsum=%d, rsum=%d, msum=%d
", l, r, lsum[rt], rsum[rt], msum[rt]);
    if(l==r) return ;
    int m=l+r>>1;
    print(ls); print(rs);
}

int main()
{
    ios::sync_with_stdio(0); cin.tie(0); cout.tie(0);
    //freopen("in.txt", "r", stdin);
    int n, m;
    cin>>n>>m;
    build(1, n, 1);
    while(m--)
    {
        int cmd, a, b;
        cin>>cmd;
        if(cmd==1){
            cin>>a; 
            if(msum[1]<a){
                cout<<"0
"; continue;
            }
            int pos=query(a, 1, n, 1);
            cout<<pos<<"
";
            update(pos, pos+a-1, 1, 1, n, 1);
        } 
        else{
            cin>>a>>b;
            update(a, a+b-1, 0, 1, n, 1);
        }
//        printf("
");
//        print(1, n, 1);
//        printf("
");
    }    
    return 0;
} 
View Code
原文地址:https://www.cnblogs.com/Yokel062/p/11352683.html