线段树的应用及模版

线段树的应用:

1)求面积:

一.坐标离散化;

二.垂直边按x坐标排序;

三.从左往右用线段树处理垂直边,累计每个离散x区间长度和线段树长度的乘积。

2)求周长:

一.坐标离散化;

二.垂直边按x坐标排序,第二关键字为入边优于出边;

三.从左往右用线段树处理垂直边,在每个离散点上先加入所有入边,累计线段树长度变化值,再删除所有出边,累计线段树长度变化值;

四.水平边按y坐标排序,第二关键字为入边优于出边;

五.从上往下用线段树处理水平边,在每个离散点上先加入所有入边,累计线段树长度变化值,再删除所有出边,累计线段树长度变化值。

 

基本线段树代码:

//基本线段树,可以处理加入边和删除边不同的情况

//t是传入的线段树的根节点

//l0, r0是传入的线段树的节点范围

//l,r是线段树中的一个区间的两个端点

#define MAXN 10000

 

struct segtree

{

       int n;

       int cnt[MAXN];   //覆盖这一节点区间的线段的个数

int len[MAXN];   //区间的长度

       segtree(int t) : n(t)

       {

              for(int i=1; i<=t;i++)     //初始化数组

                     cnt[i] = len[i] = 0;

       };

 

       //成员函数啦

       void update(int t, int l, int r); //更新以t为根节点的线段树的区间长度len[t]

       void inc_seg(int t, int l0, int r0, int l, int r);  //加入边

       void dec_seg(int t, int l0, int r0, int l, int r); //删除边

       int seg_len(int t, int l0, int r0, int l, int r);     //求线段树的长度

};

 

//区间(l, r)的长度

int length(int l, int r)

{

       return (r - l);

}

 

//更新以t为根节点的线段树的区间长度len[t]

void segtree::update(int t, int l, int r)

{

       if(cnt[t] || (r-l)==1)

              len[t] = length(l, r);

       else

              len[t] = len[t+t] + len[t+t+1];        //左右子树区间长度之和

}

 

//向以t为根节点、区间为(l0, r0)的线段树中加入区间(l, r)

void segtree::inc_seg(int t, int l0, int r0, int l, int r)

{

       if(l0 == l && r0 == r)

       {

              cnt[t]++;

       }

       else

       {

              int m0 = (l0 +r0)>>1;          //即(l0+r0)/2

              if(l < m0)     //覆盖到左孩子区间

              {

                     //向左孩子区间插入(l, r)

                     inc_seg(t+t, l0, m0, l, m0<r?m0:r);

              }

              if(r>m0)      //覆盖到右孩子区间

              {

                     //向右孩子区间插入(l, r)

                     inc_seg(t+t+1, m0, r0, m0>l?m0:l, r);

              }

              if(cnt[t+t] && cnt[t+t+1])

              {

                     cnt[t+t]--;

                     update(t+t, l0, m0);

                     cnt[t+t+1]--;

                     update(t+t+1, m0, r0);

                     cnt[t]++;

              }

       }

       update(t, l0, r0);

}

 

//向以t为根节点、区间为(l0, r0)的线段树中删除区间(l, r)

void segtree::dec_seg(int t, int l0, int r0, int l, int r)

{

       if(l0 == l && r0 == r)

              cnt[t]--;

       else if(cnt[t])

       {

              cnt[t]--;

              if(l>l0)

                     inc_seg(t, l0, r0, l0, l);       //抵消上面的cnt[t]--

              if(r<r0)

                     inc_seg(t, l0, r0, r, r0);      //同上

       }

       else

       {

              int m0 = (l0 +r0)>>1;

              if(l<m0)

                     dec_seg(t+t, l0, m0, l, m0<r?m0:r);

              if(r>m0)

                     dec_seg(t+t+1, m0, r0, m0>l?m0:l, r);

       }

       update(t, l0, r0);

}

 

//求线段树中区间(l, r)的区间长度,(l0, r0)是线段树的初始节点区间

int segtree::seg_len(int t, int l0, int r0, int l, int r)

{

       if(cnt[t] || (l0==l && r0==r))

              return len[t];

       else

       {

              int m0 =(l0+r0)>>1;

              int ret = 0;

              if(l<m0)

                     ret += seg_len(t+t, l0, m0, l,m0<r?m0:r);

              if(r>m0)

                     ret += seg_len(t+t+1, m0, r0, l,m0>l?m0:l, r);

 

              return ret;

       }

}

 

线段树的扩展代码如下:

//线段树扩展,可以计算长度和线段树

//可以处理加入边和删除边不同的情况

//t是传入的线段树的根节点

//l0, r0是传入的线段树的节点范围

//l,r是线段树中的一个区间的两个端点

#define MAXN 10000

 

struct segtree

{

       int n;

       int cnt[MAXN]; //覆盖这一节点区间的线段的个数

int len[MAXN];   //区间的长度

       int cut[MAXN];   //存放线段数

       int bl[MAXN];     //标识左端点是否被区间覆盖到

       int br[MAXN];    //标识右端点是否被区间覆盖到

       segtree(int t) : n(t)

       {

              for(int i=1; i<=t;i++)     //初始化数组

                     cnt[i] = len[i] = cut[i] = bl[i] = br[i] =0;

       };

 

       //函数啦

       void update(int t, int l, int r);  //更新以t为根节点的线段树的区间长度len[t]

       void inc_seg(int t, int l0, int r0, int l, int r);  //加入边

       void dec_seg(int t, int l0, int r0, int l, int r); //删除边

       int seg_len(int t, int l0, int r0, int l, int r);     //求区间的长度

       int seg_cut(int t, int l0, int r0, int l, int r);     //求线段数

};

 

 

int length(int l, int r)

{

       return (r - l);

}

//更新以t为根节点、区间为[l, r]的线段树的区间长度len[t]

//和线段数cut[t]及其左右端点标识bl[t]和br[t]

void segtree::update(int t, int l, int r)

{

       if(cnt[t] || (r-l)==1)

       {

              len[t] = length(l, r);

              cut[t] = bl[t] = br[t] = 1;

       }

       else

       {

              len[t] = len[t+t] + len[t+t+1];        //左右子树长度之和

              cut[t] = cut[t+t] + cut[t+t+1];

              if(br[t+t] && bl[t+t+1])

                     cut[t]--;

              bl[t] = bl[t+t];

              br[t] = br[t+t+1];

       }

}

//向以t为根节点、区间为(l0, r0)的线段树中加入区间(l, r)

void segtree::inc_seg(int t, int l0, int r0, int l, int r)

{

       if(l0 == l && r0 == r)

       {

              cnt[t]++;

       }

       else

       {

              int m0 = (l0 +r0)>>1;          //即(l0+r0)/2

 

              if(l < m0)     //覆盖到左孩子区间

              {

                     //向左孩子区间插入(l, r)

                     inc_seg(t+t, l0, m0, l, m0<r?m0:r);

              }

 

              if(r>m0)      //覆盖到右孩子区间

              {

                     //向右孩子区间插入(l, r)

                     inc_seg(t+t+1, m0, r0, m0>l?m0:l, r);

              }

 

              if(cnt[t+t] && cnt[t+t+1])

              {

                     cnt[t+t]--;

                     update(t+t, l0, m0);

                     cnt[t+t+1]--;

                     update(t+t+1, m0, r0);

                     cnt[t]++;

              }

       }

       update(t, l0, r0);

}

//向以t为根节点、区间为(l0, r0)的线段树中删除区间(l, r)

void segtree::dec_seg(int t, int l0, int r0, int l, int r)

{

       if(l0 == l && r0 == r)

              cnt[t]--;

       else if(cnt[t])

       {

              cnt[t]--;

              if(l>l0)

                     inc_seg(t, l0, r0, l0, l);       //抵消上面的cnt[t]--

              if(r<r0)

                     inc_seg(t, l0, r0, r, r0);      //同上

       }

       else

       {

              int m0 = (l0 +r0)>>1;

              if(l<m0)

                     dec_seg(t+t, l0, m0, l, m0<r?m0:r);

              if(r>m0)

                     dec_seg(t+t+1, m0, r0, m0>l?m0:l, r);

       }

       update(t, l0, r0);

}

//求线段树中区间(l, r)的区间长度,(l0, r0)是线段树的初始节点区间

int segtree::seg_len(int t, int l0, int r0, int l, int r)

{

       if(cnt[t] || (l0==l && r0==r))

              return len[t];

       else

       {

              int m0 =(l0+r0)>>1;

              int ret = 0;

              if(l<m0)

                     ret += seg_len(t+t, l0, m0, l, m0<r?m0:r);

              if(r>m0)

                     ret += seg_len(t+t+1, m0, r0, l,m0>l?m0:l, r);

 

              return ret;

       }

}

 

//求线段树中区间(l, r)的连续线段数,(l0, r0)是线段树的初始节点区间

int segtree::seg_cut(int t, int l0, int r0, int l, int r)

{

       if(cnt[t])

              return 1;

       if(l0 == l && r0 == r)

              return cut[t];

       else

       {

              int m0 =(l0+r0)>>1;

              int ret = 0;

              if(l < m0)

              {

                     ret += seg_cut(t+t, l0, m0, l,m0<r?m0:r);

              }

              if(r > m0)

              {

                     ret += seg_cut(t+t+1, m0, r0, m0>l?m0:l,r);

              }

              if(l<m0 && r>m0 &&br[t+t] && bl[t+t+1])

                     ret--;

              return ret;

       }

}

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值