一、最简单的应用就是记录线段有否被覆盖,并随时查询当前被覆盖线段的总长度。那么此时可以在结点结构中加入一个变量int count;代表当前结点代表的子树中被覆盖的线段长度和。这样就要在插入(删除)当中维护这个count值,于是当前的覆盖总值就是根
节点的count值了。
二、另外也可以将count换成bool cover;支持查找一个结点或线段是否被覆盖。
三、实际上,通过在结点上记录不同的数据,线段树还可以完成很多不同的任务。例如,如果每次插入操作是在一条线段上每个位置均加k,而查询操作是计算一条线段上的总和,那么在结点上需要记录的值为sum。
这里会遇到一个问题:为了使所有sum值都保持正确,每一次插入操作可能要更新O(N)个sum值,从而使时间复杂度退化为O(N)。
解决方案是
Lazy思想:对整个结点进行的操作,先在结点上做标记,而并非真正执行,直到根据查询操作的需要分成两部分。
根据Lazy思想,我们可以在不代表原线段的结点上增加一个值toadd,即为对这个结点,留待以后执行的插入操作k值的总和。对整个结点插入时,只更新sum和toadd值而不向下进行,这样时间复杂度可证明为O(logN)。
对一个toadd值为0的结点整个进行查询时,直接返回存储在其中的sum值;而若对toadd不为0的一部分进行查询,则要更新其左右子结点的sum值,然后把toadd值传递下去,再对这个查询本身,左右子结点分别
递归下去。时间复杂度也是O(logN)。
模板代码:求逆序数
//模板 线段树求循环逆序数序列最小值 hdu1394
#include<iostream>
#include<stdio.h>
#define LL(x) ((x)<<1) //两倍;
#define RR(x) ((x)<<1|1) //两倍+1;
using namespace std;
struct Seg_Tree {
int left,right,val;
int calmid() {
return (left+right)/2;
}
}tt[15000];
int val[5001];
void build(int left,int right,int idx) { //idx 序号从1开始
tt[idx].left = left;
tt[idx].right = right;
tt[idx].val = 0;
if(left == right) return;
int mid = tt[idx].calmid();
build(left,mid,LL(idx));
build(mid+1,right,RR(idx));
}
//查询[left,right]中数的个数,不要把left,right看作序号,要看做数;
int query(int left,int right,int idx)
{
if(left == tt[idx].left && right == tt[idx].right)
return tt[idx].val;
int mid = tt[idx].calmid();
if(right <= mid)
{
return query(left,right,LL(idx));
}
else if(mid < left)
{
return query(left,right,RR(idx));
}
else
{
return query(left,mid,LL(idx)) + query(mid+1,right,RR(idx));
}
}
//更新所有包含id这个数的区间的val值,都+1;
void update(int id,int idx)
{
tt[idx].val ++;
if(tt[idx].left == tt[idx].right) return;
int mid = tt[idx].calmid();
if(id <= mid)
{
update(id,LL(idx));
}
else
{
update(id,RR(idx));
}
}
int main()
{
int i,n;
while(scanf("%d",&n) == 1)
{
build(0,n-1,1);
//for(i=0;i<n;i++)
int sum = 0;
for(i=0;i<n;i++)
{
scanf("%d",&val[i]);
//printf("%d\n",query(val[i],n-1,1));
sum += query(val[i],n-1,1); //1是根
update(val[i],1);//1是根
}
int ret = sum;
for(i=0;i<n;i++)
{
sum = sum - val[i] + (n - val[i] - 1);
ret=min(ret,sum);
}
printf("%d\n",ret);
}
return 0;
}
#include<iostream>
#include<stdio.h>
#define LL(x) ((x)<<1) //两倍;
#define RR(x) ((x)<<1|1) //两倍+1;
using namespace std;
struct Seg_Tree {
int left,right,val;
int calmid() {
return (left+right)/2;
}
}tt[15000];
int val[5001];
void build(int left,int right,int idx) { //idx 序号从1开始
tt[idx].left = left;
tt[idx].right = right;
tt[idx].val = 0;
if(left == right) return;
int mid = tt[idx].calmid();
build(left,mid,LL(idx));
build(mid+1,right,RR(idx));
}
//查询[left,right]中数的个数,不要把left,right看作序号,要看做数;
int query(int left,int right,int idx)
{
if(left == tt[idx].left && right == tt[idx].right)
return tt[idx].val;
int mid = tt[idx].calmid();
if(right <= mid)
{
return query(left,right,LL(idx));
}
else if(mid < left)
{
return query(left,right,RR(idx));
}
else
{
return query(left,mid,LL(idx)) + query(mid+1,right,RR(idx));
}
}
//更新所有包含id这个数的区间的val值,都+1;
void update(int id,int idx)
{
tt[idx].val ++;
if(tt[idx].left == tt[idx].right) return;
int mid = tt[idx].calmid();
if(id <= mid)
{
update(id,LL(idx));
}
else
{
update(id,RR(idx));
}
}
int main()
{
int i,n;
while(scanf("%d",&n) == 1)
{
build(0,n-1,1);
//for(i=0;i<n;i++)
int sum = 0;
for(i=0;i<n;i++)
{
scanf("%d",&val[i]);
//printf("%d\n",query(val[i],n-1,1));
sum += query(val[i],n-1,1); //1是根
update(val[i],1);//1是根
}
int ret = sum;
for(i=0;i<n;i++)
{
sum = sum - val[i] + (n - val[i] - 1);
ret=min(ret,sum);
}
printf("%d\n",ret);
}
return 0;
}