【基础】线段树

超详解线段树(浅显易懂,几乎涵盖所有线段树类型讲解,匠心之作,图文并茂)-CSDN博客

线段树tr数组开4n

深度logn 

建树

 
void bui(int id,int l,int r)//创建线段树,id表示存储下标,区间[L,r]
{
  if(l == r)//左端点等于右端点,即为叶子节点(区间长度为1),直接赋值即可
  {
    tr[id] = a[l];
    return ;
  }
// 否则将当前区间中间拆开成两个区间
  int mid = (l + r) / 2;//mid则为中间点,左儿子的结点区间为[l,mid],右儿子的结点区间为[mid + 1,r]
  bui(id * 2,l,mid); //递归构造左儿子结点
  bui(id * 2 + 1,mid + 1,r); //递归构造右儿子结点
// 左右两个区间计算完成以后
// 合并到当前区间
  tr[id] = min(tr[id * 2],tr[id * 2 + 1]);//更新父节点
}

区间查询

 
//id 表示树节点编号,l r 表示这个节点所对应的区间
//x y表示查询的区间
int find(int id,int l,int r,int x,int y)
{
  //需要查询的区间[x,y]将当前区间[l,r]包含的时候
  if(x <= l && r <= y) return tr[id];
  int mid = (l + r) / 2,ans = -INT_MAX;
   // 如果需要查询左半区间
  if(x <= mid) ans = min(ans,find(id * 2,l,mid,x,y));   
  // 如果需要查询右半区间
  if(y > mid) ans = min(ans,find(id * 2 + 1,mid + 1,r,x,y));
  return ans;
}

单点修改

找到点,修改了往回走

返回的路上沿路修改父节点


// id 表示树节点编号,l r 表示这个节点所对应的区间
// 将 a[x] 修改为 v
// 线段树单点更新

void gexi(int id, int l, int r, int x, int v)
{
// 找到长度为 1 的区间才返回
  if (l == r)
  {
      //这一棵子树的值变成了v
      //这棵子树大小为1
      //最终肯定是要实现区间修改的
    tr[id] = v;
    return;
  }


//否则找到 x 在左区间或者右区间去更新
  int mid = (l + r) / 2;
  //[l,mid]
  if (x <= mid) gexi(id * 2, l, mid, x, v);// 需要修改的值在左区间
  //[mid+1,r]
  else gexi(id * 2 + 1, mid + 1, r, x, v);// 需要修改的值在右区间
  //依然是以区间中的最大值为例

  tr[id] = max(tr[id * 2], tr[id * 2 + 1]);
}


区间修改


void push_up(int id)
{
  sumv[id] = sumv[id * 2] + sumv[id * 2 + 1];
}
void push_down(int id,int l,int r)
{
  if(lazy[id])//如果id有lazy标记
  {


    int mid = (l + r) / 2;

//[l,r]中每个a[x]加上值lazy[id]


//lazy是差分
//sumv是区间和,要求的
    //左区间和右区间都加上
    lazy[id * 2] += lazy[id];//将它的左孩子的lazy加上它的lazy
    lazy[id * 2 + 1] += lazy[id];//将它的右孩子的lazy加上它的lazy

    sumv[id * 2] += lazy[id] * (mid - l + 1);//左孩子的Q+=它下放的Q*区间长度
    sumv[id * 2 + 1] += lazy[id] * (r - mid);
    lazy[id] = 0;//清空lazy标记
  }
}

//钱将更新
void qjgx(int id,int l,int r,int x,int y,int v)//id:目前查到的节点编号  目前区间为[l,r]  目标是讲[x,y]的所有数+v
{
  if(l >= x && r <= y)//[l,r]被[x,y]包含了
  {
    lazy[id] += v;//暂时不下放Q,加进lazy标记中
    sumv[id] += v * (r - l + 1);//将Q收入囊中
    return ;
  }

  push_down(id,l,r);//要来更新下面节点了,赶紧下放Q
  int mid = (l + r) / 2;
  if(x <= mid) qjgx(id * 2,l,mid,x,y,v);//因为只有x<=mid(即[l,mid]有一部分是被[x,y]覆盖了的)才需要去更新[l,mid]
  if(y > mid) qjgx(id * 2 + 1,mid + 1,r,x,y,v);
  push_up(id);//子节点更新完之后父节点当然也要更新(上升操作)
}

mark一下

#include<iostream>
#include<map>
using namespace std;
#define  maxn 10000
#define ll long long
//那也就是区间加上1
//查询》=k

//不能这样
//否则不也是开了一个这么大的数组吗


//根据编号还原左右端点
//1 [-1e9,1e9]
//2 [-1e9,0] 3[1,1e9]
//奇数[fa.mid+1,fa.r]

int n,k;
ll ans=0;
map<ll,ll> l;
map<ll,ll>r;
ll ansl[1000001];
ll ansr[1000001];
int lazy[1000001];
int tr[1000001];
void bui(int id,ll l1,ll r1)//创建线段树,id表示存储下标,区间[L,r]
{
    //1 -10000 100000
    //2 -10000 0
    //4

    //-100000 999999
    //cout<<l1<<" "<<r1<<endl;
    l[id]=l1;
    r[id]=r1;
  if(l1 == r1)//左端点等于右端点,即为叶子节点(区间长度为1),直接赋值即可
  {
   // tr[id] = 0;
    return ;
  }
// 否则将当前区间中间拆开成两个区间


  int mid = (l1 + r1) / 2;//mid则为中间点,左儿子的结点区间为[l,mid],右儿子的结点区间为[mid + 1,r]
if((l1+r1)%2&&l1+r1<0){
    mid--;
}
  bui(id * 2,l1,mid); //递归构造左儿子结点
  bui(id * 2 + 1,mid + 1,r1); //递归构造右儿子结点
// 左右两个区间计算完成以后
// 合并到当前区间
  //tr[id] = min(tr[id * 2],tr[id * 2 + 1]);//更新父节点
}



void push_up(int id)
{
  //sumv[id] = sumv[id * 2] + sumv[id * 2 + 1];
}
void push_down(int id,int l,int r)
{
  if(lazy[id])//如果id有lazy标记
  {


    int mid = (l + r) / 2;

//[l,r]中每个a[x]加上值lazy[id]


//lazy是差分
//sumv是区间和,要求的
    //左区间和右区间都加上
    lazy[id * 2] += lazy[id];//将它的左孩子的lazy加上它的lazy
    lazy[id * 2 + 1] += lazy[id];//将它的右孩子的lazy加上它的lazy

    //sumv[id * 2] += lazy[id] * (mid - l + 1);//左孩子的Q+=它下放的Q*区间长度
    //sumv[id * 2 + 1] += lazy[id] * (r - mid);
    //lazy[id] = 0;//清空lazy标记
  }
}

//钱将更新
void qjgx(int id,int l,int r,int x,int y,int v)//id:目前查到的节点编号  目前区间为[l,r]  目标是讲[x,y]的所有数+v
{
  if(l >= x && r <= y)//[l,r]被[x,y]包含了
  {
    lazy[id] += v;//暂时不下放Q,加进lazy标记中
   // sumv[id] += v * (r - l + 1);//将Q收入囊中
    return ;
  }

  push_down(id,l,r);//要来更新下面节点了,赶紧下放Q
  int mid = (l + r) / 2;
  if(x <= mid) qjgx(id * 2,l,mid,x,y,v);//因为只有x<=mid(即[l,mid]有一部分是被[x,y]覆盖了的)才需要去更新[l,mid]
  if(y > mid) qjgx(id * 2 + 1,mid + 1,r,x,y,v);
  push_up(id);//子节点更新完之后父节点当然也要更新(上升操作)
}

void dfs(int id){

//如果区间中的值全>=k
//返回ans++
//或许可以维护区间中的最小值
if(tr[id]>=k){
        return ;
    ans++;
    ansl[ans]=l[id];
    ansr[ans]=r[id];
}
dfs(id*2);
dfs(id*2+1);
}

/*//看样子这是要某个区间内的东西
//而我是要筛选
//id 表示树节点编号,l r 表示这个节点所对应的区间
//x y表示查询的区间
int findd(int id,int l,int r,int x,int y)
{
  //需要查询的区间[x,y]将当前区间[l,r]包含的时候
  if(x <= l && r <= y) return tr[id];
  int mid = (l + r) / 2,ans = -INT_MAX;
   // 如果需要查询左半区间
  if(x <= mid) ans = min(ans,findd(id * 2,l,mid,x,y));
  // 如果需要查询右半区间
  if(y > mid) ans = min(ans,findd(id * 2 + 1,mid + 1,r,x,y));
  return ans;
}*/






int main(){

scanf("%d%d",&n,&k);

bui(1,-maxn,maxn);

for(int i=1;i<=n;i++){
        ll x,y;
    scanf("%lld%lld",&x,&y);
    qjgx(1,-maxn,maxn,x,y,1);
}

/*//总区间数记成tot
for(ll i=1;i<=tot;i++){
    //必须递归向下

}*/
dfs(1);

cout<<ans<<endl;
for(int i=1;i<=ans;i++){
    cout<<ansl[i]<<" "<<ansr[i]<<endl;

}

}

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值