首页 > 代码库 > 线段树题目汇总

线段树题目汇总

区间合并部分:

POJ 3667 Hotel

求某大于等于a的最长区间

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#define LEN 50001<<2
using namespace std;
struct Line_tree
{
    //分别表示以当前区间为左端点的最长连续空白区间,为右端点的最长连续空白区间,该区间的最长连续空白区间,该区间的懒惰标记
    int lt[LEN],rt[LEN],maxt[LEN],tar[LEN];
    void build(int o,int L,int R)
    {
        lt[o]=rt[o]=maxt[o]=R-L+1;
        tar[o]=-1;
        if(L==R) return ;
        int M=L+(R-L)/2;
        build(o<<1,L,M);
        build(o<<1|1,M+1,R);
    }
    void push_down(int o,int m)
    {
        if(tar[o]!=-1)
        {
            tar[o<<1]=tar[o<<1|1]=tar[o];
            if(tar[o]==1) lt[o<<1]=rt[o<<1]=maxt[o<<1]=lt[o<<1|1]=rt[o<<1|1]=maxt[o<<1|1]=0;
            else
            {
                lt[o<<1]=rt[o<<1]=maxt[o<<1]=m-(m>>1);
                lt[o<<1|1]=rt[o<<1|1]=maxt[o<<1|1]=m>>1;
            }
            tar[o]=-1;
        }
    }
    void push_up(int o,int m)
    {
        if(lt[o<<1]==m-(m>>1)) lt[o]=lt[o<<1]+lt[o<<1|1];
        else lt[o]=lt[o<<1];
        if(rt[o<<1|1]==m>>1) rt[o]=rt[o<<1|1]+rt[o<<1];
        else rt[o]=rt[o<<1|1];
        maxt[o]=max(rt[o<<1]+lt[o<<1|1],max(maxt[o<<1],maxt[o<<1|1]));
    }
    int query(int len,int L,int R,int o)
    {
        if(L==R) return L;
        push_down(o,R-L+1);
        int M=L+(R-L)/2;
        if(maxt[o<<1]>=len) return query(len,L,M,o<<1);
        else if(rt[o<<1]+lt[o<<1|1]>=len) return M-rt[o<<1]+1;
        else return query(len,M+1,R,o<<1|1);
    }
    void update(int ql,int qr,int c,int L,int R,int o)
    {
        if(ql<=L&&R<=qr)
        {
            if(c==1) lt[o]=rt[o]=maxt[o]=0;
            else lt[o]=rt[o]=maxt[o]=R-L+1;
            tar[o]=c;
        }
        else
        {
            push_down(o,R-L+1);
            int M=L+(R-L)/2;
            if(ql<=M) update(ql,qr,c,L,M,o<<1);
            if(M<qr) update(ql,qr,c,M+1,R,o<<1|1);
            push_up(o,R-L+1);
        }
    }
};
Line_tree tree;
int main()
{
    int n,m;
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        tree.build(1,1,n);
        while(m--)
        {
            int p,a,b;
            scanf("%d",&p);
            if(p==1)
            {
                scanf("%d",&a);
                if(a>tree.maxt[1]) puts("0");
                else
                {
                    int pos=tree.query(a,1,n,1);
                    printf("%d\n",pos);
                    tree.update(pos,pos+a-1,1,1,n,1);
                }
            }
            else
            {
                scanf("%d%d",&a,&b);
                tree.update(a,a+b-1,0,1,n,1);
            }
        }
    }
    return 0;
}
View Code

 

HDU 3308 LCIS

求某区间最长子串长度。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#define LEN 100001<<2
using namespace std;
int arr[100005];
struct Line_tree
{
    int lt[LEN],rt[LEN],maxt[LEN];
    void push_up(int o,int L,int R)
    {
        int m=R-L+1,M=L+(R-L)/2;
        if(lt[o<<1]==m-(m>>1)&&arr[M]<arr[M+1]) lt[o]=lt[o<<1]+lt[o<<1|1];
        else lt[o]=lt[o<<1];
        if(rt[o<<1|1]==(m>>1)&&arr[M]<arr[M+1]) rt[o]=rt[o<<1]+rt[o<<1|1];
        else rt[o]=rt[o<<1|1];
        maxt[o]=max(maxt[o<<1],maxt[o<<1|1]);
        if(arr[M]<arr[M+1]) maxt[o]=max(maxt[o],rt[o<<1]+lt[o<<1|1]);
    }
    void build(int o,int L,int R)
    {
        if(L==R)
            lt[o]=rt[o]=maxt[o]=1;
        else
        {
            int M=L+(R-L)/2;
            build(o<<1,L,M);
            build(o<<1|1,M+1,R);
            push_up(o,L,R);
        }
    }
    void update(int o,int pos,int val,int L,int R)
    {
        if(L==R) arr[L]=val;
        else
        {
            int M=L+(R-L)/2;
            if(pos<=M) update(o<<1,pos,val,L,M);
            else update(o<<1|1,pos,val,M+1,R);
            push_up(o,L,R);
        }
    }
    int query(int o,int ql,int qr,int L,int R)
    {
        if(ql<=L&&R<=qr) return maxt[o];
        else
        {
            int M=L+(R-L)/2;
            int maxn=0;
            if(ql<=M) maxn=max(maxn,query(o<<1,ql,qr,L,M));
            if(M<qr) maxn=max(maxn,query(o<<1|1,ql,qr,M+1,R));
            if(ql<=M&&M<qr&&arr[M]<arr[M+1]) maxn=max(maxn,min(M+lt[o<<1|1],qr)-max(M-rt[o<<1]+1,ql)+1);
            return maxn;
        }
    }
};
Line_tree tree;
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i=1; i<=n; ++i)
            scanf("%d",&arr[i]);
        tree.build(1,1,n);
        while(m--)
        {
            char p[3];
            int a,b;
            scanf("%s%d%d",p,&a,&b);
            if(p[0]==Q)
            {
                a++;
                b++;
                printf("%d\n",tree.query(1,a,b,1,n));
            }
            else
            {
                a++;
                tree.update(1,a,b,1,n);
            }
        }
    }
    return 0;
}
View Code

 

HDU 3397 Sequence operation

要用两个懒惰标记来做。

同时维护区间1的个数(这个比较好做),另外还要维护区间连续的最长1和0的长度(各开三个数组,分别是以区间左端点为左端点、以区间右端点为右端点和区间最大长度三种情况)。置1和置0的情况使用同一个懒惰标记,这个比较好做。难点在于异或的时候,如果在某个结点执行异或操作,如果该结点存在非空的置1/0的懒惰标记,这时候就把该非空的

1/0的懒惰标记颠倒,即置1变成置0,置0变成置1,否则的话就标记一个异或标记。这个时候要把最长的1和0的长度交换。

#include <iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<string>
#define LEN 100001<<2
using namespace std;
int arr[100001];
struct Line_tree
{
    int lz[LEN],rz[LEN],maxz[LEN];
    int lo[LEN],ro[LEN],maxo[LEN];
    int tar[LEN],txr[LEN],sum[LEN];
    void fill_val(int o,int c,int L,int R)
    {
        tar[o]=c;
        txr[o]=0;
        if(c==0)
        {
            lz[o]=rz[o]=maxz[o]=R-L+1;
            lo[o]=ro[o]=maxo[o]=0;
            sum[o]=0;
        }
        else
        {
            lz[o]=rz[o]=maxz[o]=0;
            lo[o]=ro[o]=maxo[o]=R-L+1;
            sum[o]=R-L+1;
        }
    }
    void fill_xor(int o,int L,int R)
    {
        if(tar[o]!=-1) tar[o]^=1;
        else txr[o]^=1;
        sum[o]=R-L+1-sum[o];
        swap(lo[o],lz[o]);
        swap(ro[o],rz[o]);
        swap(maxo[o],maxz[o]);
    }
    void push_down(int o,int L,int R)
    {
        int m=R-L+1,M=L+(R-L)/2;
        if(tar[o]!=-1)
        {
            fill_val(o<<1,tar[o],L,M);
            fill_val(o<<1|1,tar[o],M+1,R);
            tar[o]=-1;
        }
        if(txr[o])
        {
            fill_xor(o<<1,L,M);
            fill_xor(o<<1|1,M+1,R);
            txr[o]=0;
        }
    }
    void push_up(int o,int L,int R)
    {
        sum[o]=sum[o<<1]+sum[o<<1|1];
        int M=L+(R-L)/2,m=R-L+1;

        if(lo[o<<1]==m-(m>>1)) lo[o]=lo[o<<1]+lo[o<<1|1];
        else lo[o]=lo[o<<1];
        if(ro[o<<1|1]==(m>>1)) ro[o]=ro[o<<1]+ro[o<<1|1];
        else ro[o]=ro[o<<1|1];
        maxo[o]=max(maxo[o<<1],maxo[o<<1|1]);
        maxo[o]=max(maxo[o],ro[o<<1]+lo[o<<1|1]);

        if(lz[o<<1]==m-(m>>1)) lz[o]=lz[o<<1]+lz[o<<1|1];
        else lz[o]=lz[o<<1];
        if(rz[o<<1|1]==(m>>1)) rz[o]=rz[o<<1]+rz[o<<1|1];
        else rz[o]=rz[o<<1|1];
        maxz[o]=max(maxz[o<<1],maxz[o<<1|1]);
        maxz[o]=max(maxz[o],rz[o<<1]+lz[o<<1|1]);
    }
    void build(int o,int L,int R)
    {
        tar[o]=-1;
        txr[o]=0;
        if(L==R)
        {
            if(arr[L]==1)
            {
                sum[o]=lo[o]=ro[o]=maxo[o]=1;
                lz[o]=rz[o]=maxz[o]=0;
            }
            else
            {
                sum[o]=lo[o]=ro[o]=maxo[o]=0;
                lz[o]=rz[o]=maxz[o]=1;
            }
        }
        else
        {
            int M=L+(R-L)/2;
            build(o<<1,L,M);
            build(o<<1|1,M+1,R);
            push_up(o,L,R);
        }
    }
    void update(int o,int c,int ql,int qr,int L,int R)
    {
        if(ql<=L&&R<=qr)
        {
            if(c<2) fill_val(o,c,L,R);
            else if(c==2) fill_xor(o,L,R);
        }
        else
        {
            push_down(o,L,R);
            int M=L+(R-L)/2;
            if(ql<=M) update(o<<1,c,ql,qr,L,M);
            if(M<qr) update(o<<1|1,c,ql,qr,M+1,R);
            push_up(o,L,R);
        }
    }
    int query(int o,int c,int ql,int qr,int L,int R)
    {
        if(ql<=L&&R<=qr)
            return c==4?maxo[o]:sum[o];
        else
        {
            push_down(o,L,R);
            int M=L+(R-L)/2,m=R-L+1,maxn=0,s=0;
            if(c==4)
            {
                if(ql<=M) maxn=max(maxn,query(o<<1,c,ql,qr,L,M));

                if(M<qr) maxn=max(maxn,query(o<<1|1,c,ql,qr,M+1,R));
                if(ql<=M&&M<qr) maxn=max(maxn,min(qr,M+lo[o<<1|1])-max(ql,M-ro[o<<1]+1)+1);
                return maxn;
            }
            else
            {
                if(ql<=M) s+=query(o<<1,c,ql,qr,L,M);
                if(M<qr) s+=query(o<<1|1,c,ql,qr,M+1,R);
                return s;
            }
        }
    }
};
Line_tree tree;
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {
        int n,m;
        scanf("%d%d",&n,&m);
        for(int i=1; i<=n; ++i)
            scanf("%d",&arr[i]);
        tree.build(1,1,n);
        while(m--)
        {
            int a,b,c;
            scanf("%d%d%d",&a,&b,&c);
            b++;
            c++;
            if(a<=2) tree.update(1,a,b,c,1,n);
            else printf("%d\n",tree.query(1,a,b,c,1,n));
        }
    }
    return 0;
}
View Code

 

UPC 2555 Longest Non-decreasing Substring

同时维护区间非递增和非递减的最长区间长度。另外要再开两个数组分别维护当前区间的左端点取值和右端点取值,它们的值分别在push_up的时候来自子节点的值。

#include <cstdio>
#include <cstring>
#include <algorithm>
#include <iostream>
#define LEN 100001<<2
using namespace std;
char str[100005];
int n,m;
struct Line_Tree
{
    int lu[LEN],ru[LEN],maxu[LEN];
    int ld[LEN],rd[LEN],maxd[LEN];
    int left[LEN],right[LEN];
    int tar[LEN];
    int filp(int x)
    {
        return 9-x;
    }
    void fill_xor(int o,int L,int R)
    {
        int M=L+(R-L)/2;
        tar[o]^=1;
        left[o]=filp(left[o]);
        right[o]=filp(right[o]);
        swap(lu[o],ld[o]);
        swap(ru[o],rd[o]);
        swap(maxu[o],maxd[o]);
    }
    void push_up(int o,int L,int R)
    {
        int M=L+(R-L)/2,m=R-L+1;
        left[o]=left[o<<1];
        right[o]=right[o<<1|1];
        if(lu[o<<1]==m-(m>>1)&&right[o<<1]<=left[o<<1|1]) lu[o]=lu[o<<1]+lu[o<<1|1];
        else lu[o]=lu[o<<1];
        if(ru[o<<1|1]==(m>>1)&&right[o<<1]<=left[o<<1|1]) ru[o]=ru[o<<1]+ru[o<<1|1];
        else ru[o]=ru[o<<1|1];
        maxu[o]=max(maxu[o<<1],maxu[o<<1|1]);
        if(right[o<<1]<=left[o<<1|1]) maxu[o]=max(maxu[o],ru[o<<1]+lu[o<<1|1]);

        if(ld[o<<1]==m-(m>>1)&&right[o<<1]>=left[o<<1|1]) ld[o]=ld[o<<1]+ld[o<<1|1];
        else ld[o]=ld[o<<1];
        if(rd[o<<1|1]==(m>>1)&&right[o<<1]>=left[o<<1|1]) rd[o]=rd[o<<1]+rd[o<<1|1];
        else rd[o]=rd[o<<1|1];
        maxd[o]=max(maxd[o<<1],maxd[o<<1|1]);
        if(right[o<<1]>=left[o<<1|1]) maxd[o]=max(maxd[o],rd[o<<1]+ld[o<<1|1]);
    }
    void push_down(int o,int L,int R)
    {
        int M=L+(R-L)/2;
        if(tar[o])
        {
            fill_xor(o<<1,L,M);
            fill_xor(o<<1|1,M+1,R);
            tar[o]=0;
        }
    }
    void build(int o,int L,int R)
    {
        tar[o]=0;
        left[o]=str[L]-0;
        right[o]=str[R]-0;
        if(L==R)
        {
            lu[o]=ru[o]=maxu[o]=1;
            ld[o]=rd[o]=maxd[o]=1;
        }
        else
        {
            int M=L+(R-L)/2;
            build(o<<1,L,M);
            build(o<<1|1,M+1,R);
            push_up(o,L,R);
        }
    }
    void update(int o,int ql,int qr,int L,int R)
    {
        if(ql<=L&&R<=qr)
            fill_xor(o,L,R);
        else
        {
            push_down(o,L,R);
            int M=L+(R-L)/2;
            if(ql<=M) update(o<<1,ql,qr,L,M);
            if(M<qr) update(o<<1|1,ql,qr,M+1,R);
            push_up(o,L,R);
        }
    }
    int query()
    {
        push_down(1,1,n);
        return maxu[1];
    }
};
Line_Tree tree;
int main()
{
    int T;
    scanf("%d",&T);
    while(T--)
    {

        scanf("%d%d",&n,&m);
        scanf("%s",str+1);
        tree.build(1,1,n);
        char p[10];
        while(m--)
        {
            scanf("%s",p);
            if(p[0]==q) printf("%d\n",tree.query());
            else
            {
                int a,b;
                scanf("%d%d",&a,&b);
                tree.update(1,a,b,1,n);
            }
        }
        printf("\n");
    }
    return 0;
}
View Code

 

HDU 2871 Memory Control

比较繁琐的线段树,基本考察了各种常见操作。

注意一点reset不要用build而要用update以避免超时。

#include <iostream>
#include <cstdio>
#include <cstring>
#include <algorithm>
#define ll long long
#define LEN 50010<<2
using namespace std;
struct Line_Tree
{
    //sum维护区间内最多的块数
    //ltrtmaxt维护区间内最长空白块长度
    //left维护该块左端点
    //get二分查找第n个块,update填充和释放区内,include寻找占据元素k的块
    int sum[LEN],left[LEN],right[LEN];
    int lt[LEN],rt[LEN],maxt[LEN];
    int cov[LEN],setv[LEN];
    void fill_cov(int o,int a,int b,int c,int L,int R)
    {
        lt[o]=rt[o]=maxt[o]=a?0:R-L+1;
        left[o]=b;
        right[o]=c;
        cov[o]=a;
    }
    void push_down(int o,int L,int R)
    {
        if(cov[o]!=-1)
        {
            int M=L+(R-L)/2;
            fill_cov(o<<1,cov[o],left[o],right[o],L,M);
            fill_cov(o<<1|1,cov[o],left[o],right[o],M+1,R);
            cov[o]=-1;
        }
        if(setv[o]!=-1)
        {
            sum[o<<1]=setv[o];
            sum[o<<1|1]=setv[o];
            setv[o<<1]=setv[o<<1|1]=setv[o];
            setv[o]=-1;
        }
    }
    void push_up(int o,int L,int R)
    {
        sum[o]=sum[o<<1]+sum[o<<1|1];
        int m=R-L+1,M=L+(R-L)/2;
        if(lt[o<<1]==m-(m>>1)) lt[o]=lt[o<<1]+lt[o<<1|1];
        else lt[o]=lt[o<<1];
        if(rt[o<<1|1]==(m>>1)) rt[o]=rt[o<<1]+rt[o<<1|1];
        else rt[o]=rt[o<<1|1];
        maxt[o]=max(rt[o<<1]+lt[o<<1|1],max(maxt[o<<1],maxt[o<<1|1]));
    }
    void build(int o,int L,int R)
    {
        lt[o]=rt[o]=maxt[o]=1;
        left[o]=right[o]=0;
        cov[o]=-1;
        setv[o]=-1;
        sum[o]=0;
        if(L==R)  return ;
        else
        {
            int M=L+((R-L)>>1);
            build(o<<1,L,M);
            build(o<<1|1,M+1,R);
            push_up(o,L,R);
        }
    }
    //二分实现查找第k个块,返回该块左端点
    int get(int o,int c,int L,int R)
    {
        if(L==R) return L;
        else
        {
            push_down(o,L,R);
            int M=L+((R-L)>>1);
            if(sum[o<<1]>=c) return get(o<<1,c,L,M);
            else return get(o<<1|1,c-sum[o<<1],M+1,R);
        }
    }
    //二分实现查找第一个空白长度大于c的区间左端点
    int query(int o,int c,int L,int R)
    {
        if(L==R) return L;
        push_down(o,L,R);
        int M=L+((R-L)>>1);
        if(maxt[o<<1]>=c) return query(o<<1,c,L,M);
        else if(rt[o<<1]+lt[o<<1|1]>=c) return M-rt[o<<1]+1;
        else return query(o<<1|1,c,M+1,R);
    }
    //寻找含有k的数的左右端点
    int include(int o,int k,int L,int R)
    {
        if(L==R) return o;
        int M=L+((R-L)>>1);
        push_down(o,L,R);
        if(k<=M)  return include(o<<1,k,L,M);
        else return  include(o<<1|1,k,M+1,R);
    }
    //实现填充与清空,a表示清空或填区间左端点
    void update(int o,int a,int b,int c,int ql,int qr,int L,int R)
    {
        if(ql<=L&&R<=qr)  fill_cov(o,a,b,c,L,R);
        else
        {
            push_down(o,L,R);
            int M=L+((R-L)>>1);
            if(ql<=M) update(o<<1,a,b,c,ql,qr,L,M);
            if(M<qr)  update(o<<1|1,a,b,c,ql,qr,M+1,R);
            push_up(o,L,R);
        }
    }
    //实现对块的最左端点的清空与设置
    void set(int o,int c,int ql,int qr,int L,int R)
    {
        if(ql<=L&&R<=qr)
        {
            sum[o]=c;
            setv[o]=c;
        }
        else
        {
            push_down(o,L,R);
            int M=L+((R-L)>>1);
            if(ql<=M) set(o<<1,c,ql,qr,L,M);
            if(M<qr) set(o<<1|1,c,ql,qr,M+1,R);
            push_up(o,L,R);
        }
    }
};
Line_Tree tree;
int main()
{
    int n,m;
 // freopen("out.txt","w",stdout);
    while(scanf("%d%d",&n,&m)!=EOF)
    {
        char p[15];
        int q;
        tree.build(1,1,n);
        while(m--)
        {
            scanf("%s",p);
            if(p[0]!=R)
            {
                scanf("%d",&q);
                if(p[0]==N)
                {
                    if(tree.maxt[1]<q) puts("Reject New");
                    else
                    {
                        int pos=tree.query(1,q,1,n);
                        printf("New at %d\n",pos);
                        tree.update(1,1,pos,pos+q-1,pos,pos+q-1,1,n);
                        tree.set(1,1,pos,pos,1,n);
                    }
                }
                else if(p[0]==F)
                {
                    int x,y,pos=tree.include(1,q,1,n);
                    x=tree.left[pos];
                    y=tree.right[pos];
                    if(x)
                    {
                        printf("Free from %d to %d\n",x,y);
                        tree.update(1,0,0,0,x,y,1,n);
                        tree.set(1,0,x,x,1,n);
                    }
                    else puts("Reject Free");
                }
                else if(p[0]==G)
                {
                    if(q>tree.sum[1]) puts("Reject Get");
                    else
                    {
                        int pos=tree.get(1,q,1,n);
                        printf("Get at %d\n",pos);
                    }
                }
            }
            else
            {
                tree.update(1,0,0,0,1,n,1,n);
                tree.set(1,0,1,n,1,n);
                puts("Reset Now");
            }
        }
        printf("\n");
    }
    return 0;
}
View Code