线段树的应用:
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;
}
}