线段树单点和区间两种建树方案

线段树有两种建树方案:
1.单点
区间【1,5】,分为【1,3】【4,5】->【1,2】【3】【4】【5】(各区间边界
都不相邻,每个边界即是一个点)
PS: 如果存的是线段,要将线段用单点的形式表示,比如【1,2】区间,将左 端点+1,变成2这个点,就可以用单点这种形式的线段树。
处理方式build(l,mid); build(mid+1,r);
2.线段
正常理解即可。但建树时终止条件为l==r-1,表明一个单位区间。
处理方式,build(l,mid); build(mid,r);

例题:Count the Colors ZOJ - 1610 (线段树,区间覆盖求染色段个数)
1.单点代码:

#include 
#include 
#include 
#include 
using namespace std;
typedef long long ll;
const int maxn=1e4+7;
struct node
{
     
    int l,r,type;
} tree[maxn<<2];
int a[maxn];
int n,m;
void build(int x,int l,int r)
{
     
    tree[x].type=-1;
    tree[x].l=l,tree[x].r=r;
    if(l==r)
        return ;
    int mid=(l+r)>>1;
    build(x<<1,l,mid);
    build(x<<1|1,mid+1,r);
}
void pushdown(int x)
{
     
    if(tree[x].type!=-1)
        tree[x<<1].type=tree[x<<1|1].type=tree[x].type,tree[x].type=-1;
}
void update(int x,int L,int R,int c)
{
     
    if(L<=tree[x].l&&R>=tree[x].r)
    {
     
        tree[x].type=c;
        return ;
    }
    if(tree[x].l==tree[x].r)return ;
    pushdown(x);
    int mid=(tree[x].l+tree[x].r)>>1;
    if(L<=mid)update(x<<1,L,R,c);
    if(R>mid)update(x<<1|1,L,R,c);
}
void query(int x)
{
     
    if(tree[x].l==tree[x].r)
    {
     
        if(tree[x].type!=m&&tree[x].type!=-1)
            a[tree[x].type]++;
        m=tree[x].type;
        return ;
    }
    pushdown(x);
    query(x<<1);
    query(x<<1|1);
}
int main()
{
     
    int i,j,x,y,c;
    while(~scanf("%d",&n))
    {
     
        m=-1;
        memset(a,0,sizeof(a));
        build(1,1,8000);
        for(i=1; i<=n; i++)
        {
     
            scanf("%d%d%d",&x,&y,&c);
            if(x<y)update(1,x+1,y,c);
        }
        query(1);
        for(i=0; i<8000; i++)
        {
     
            if(a[i])
                printf("%d %d\n",i,a[i]);
        }
        printf("\n");
    }
    return 0;
}

2.区间代码:

#include 
#include 
#include 
#include 
using namespace std;
typedef long long ll;
const int maxn=1e4+7;
struct node
{
     
    int l,r,type;
} tree[maxn<<2];
int a[maxn];
int n,m;
void build(int x,int l,int r)
{
     
    tree[x].type=-1;
    tree[x].l=l,tree[x].r=r;
    if(l==r-1)
        return ;
    int mid=(l+r)>>1;
    build(x<<1,l,mid);
    build(x<<1|1,mid,r);
}
void pushdown(int x)
{
     
    if(tree[x].type!=-1)
        tree[x<<1].type=tree[x<<1|1].type=tree[x].type,tree[x].type=-1;
}
void update(int x,int L,int R,int c)
{
     
    if(L<=tree[x].l&&R>=tree[x].r)
    {
     
        tree[x].type=c;
        return ;
    }
    if(tree[x].l==tree[x].r-1)return ;//重点,保证合法,因为区间建树不存在
    //左右节点相等的节点,所以要把这种情况排除
    pushdown(x);                      //细节案例为0——1区间,mid=0,但不存
    //在左右节点都为0的节点(L=0,mid=0)
    int mid=(tree[x].l+tree[x].r)>>1;
    if(L<=mid)update(x<<1,L,R,c);
    if(R>mid)update(x<<1|1,L,R,c);//R=mid也可以
}
void query(int x)
{
     
    if(tree[x].l==tree[x].r-1)
    {
     
        if(tree[x].type!=m&&tree[x].type!=-1)
            a[tree[x].type]++;
        m=tree[x].type;
        return ;
    }
    pushdown(x);
    query(x<<1);
    query(x<<1|1);
}
int main()
{
     
    int i,j,x,y,c;
    while(~scanf("%d",&n))
    {
     
        m=-1;
        memset(a,0,sizeof(a));
        build(1,0,8000);
        for(i=1; i<=n; i++)
        {
     
            scanf("%d%d%d",&x,&y,&c);
            update(1,x,y,c);
        }
        query(1);
        for(i=0; i<=8000; i++)
        {
     
            if(a[i])
                printf("%d %d\n",i,a[i]);
        }
        printf("\n");
    }
    return 0;
}


你可能感兴趣的:(线段树单点和区间两种建树方案)