struct line
{
int left,right;//左端点,右端点
int n;//记录这条线段出现了多少次,默认为0
}a[16];
//满二叉树的性质决定a[i]的左儿子是a[2*i],右儿子是a[2*i+1]
//对于已知的性段进行插入操作
void insert(int s,int t,int step)
//要插入线段的左端点,右端点,以及当前树的某条线段
{
if(s==a[step].left&&t==a[step].right)
{
a[step].n++;//插入线段匹配,此条线段记录加1;
return;;//插入结束返回
}
if(a[step].left==a[step].right)
return ;//当前线段树没有儿子,为叶子节点
int mid=(a[step].left+a[step].right)/2;
if(mid>=t) insert(s,t,step*2);
//中点在t的右边插入到左儿子,参考a[i],a[2*i]就能理解
else if(mid<=s) insert(s,t,step+1);
//中点在t的左边插入到右儿子
else //中点在s和t之间,把待插线端分成两半分别插入到左右儿子里面
{
insert(s,mid,step*2);
insert(mid,t,step*2+1);
}
}
//比如三条线段的插入情况:
//----[2,5]
//-[2,5]与[0,7]比较,分成两部分:[2,3]插到左儿子[0,3],[4,5]插到右儿子[4,7]
//[2,3]与[0,3]比较,插到右儿子[2,3],[4,5]和[4,7]比较,插到左儿子
//[2,3]与[2,3]匹配,[2,3]记录加1,[4,5]与[4,5]匹配,[4,5]记录加1;
//---[4,6]
//[4,6]与[0,7]比较,插到右儿子[4,7];
//[4,6]与[4,7]比较,分成两部分,[4,5]插到左儿子[4,5],[6,6]插到右儿子[6,7];
//[4,5]与[4,5]匹配,[4,5]记录加1;[6,6]与[6,7]比较,插到左儿子[6,6]
//[6,6]与[6,6]匹配,[6,6]记录加1;
//----[0,7]
//[0,7]与[0,7]匹配,[0,7]记录加1
【0,7】
1
/ \
【0,3】 【4,7】
0 0
/ \ / \
【0,1】 【2,3】 【4,5】 【6,7】
0 1 2 0
/ \ / \ / \ / \
【0,0】 【1,1】 【2,2】 【3,3】 【4,4】 【5,5】 【6,6】 【7,7】
0 0 0 0 0 0 1 0
询问操作和插入操作类似,也是递归过程,略
2——依次把【0,7】 【0,3】 【2,3】 【2,2】的记录n加起来,结果为2
4——依次把【0,7】 【4,7】 【4,5】 【4,4】的记录n加起来,结果为3
7——依次把【0,7】 【4,7】 【6,7】 【7,7】的记录n加起来,结果为1
struct line
{
int left,right;//左端点,右端点
int n;//记录这条线段出现了多少次,默认为0
}a[16];
//初始化新的节点
void buildt(int l,int r,int step)
//要重建的左右端点,以及当前线段树种某条线段
{
a[step].left=l;
a[step].right=r;
a[step].n=0;
if(l==r) return ;
buildt(l,(l+r)/2,step*2);//往下建左儿子
buildt((l+r)/2,r,step*2);//往下建右儿子
}
//访问
void count(int s,int t,int step)
{
if(a[step].n!=0)
{
sum=sum+a[step].n*(t-s+1);
}
if(a[step].left==a[step].right) return;
int mid=(a[step].left+a[step].right)/2;
if(mid>=t)
count(s,t,step*2);
else if(mid<s)
count(s,t,step*2+1);
else
{
count(s,mid,step*2);
count(mid+1,t,step*2+1);
}
}