首页 > 其他分享 >线段树分裂

线段树分裂

时间:2022-11-08 20:26:01浏览次数:54  
标签:return val int 线段 sgt 分裂 root ll

#include <bits/stdc++.h>
using namespace std;
const int maxn = 2e5 + 5;
typedef long long ll;
struct Node
{
    int l,r;
    ll val;
}sgt[maxn*40];      //? 40 = 2*maxm*log2(maxn)
int cnt,root[maxn];
inline void pushup(int k) { sgt[k].val = sgt[sgt[k].l].val + sgt[sgt[k].r].val; }
void modify(int l,int r,int &k,int p,int x)     // 单点修改:p位置的值加上x,空间复杂度O(logn)
{
    if(!k) k=++cnt;     // 如果到了NIL结点就新建一个
    sgt[k].val += x;    // 单点修改的加法直接一条线上全部加上x即可
    if(l==r) return;
    int m = (l+r)>>1;
    if(p<=m) modify(l, m, sgt[k].l, p, x);      // 在左子树
    else modify(m+1, r, sgt[k].r, p, x);        // 在右子树
}
void merge(int &x,int y)        // 把y结点的内容合并到x结点上,此写法不消耗空间
{
    if(!(x&&y)) x|=y;           // 如果二者有NULL结点
    else 
    {
        sgt[x].val += sgt[y].val;   // 维护加法,直接加就是了
        merge(sgt[x].l, sgt[y].l);  // 递归合并两结点的左子树
        merge(sgt[x].r, sgt[y].r);  // 递归合并两结点的右子树
    }
}
int split(int l,int r,int &k,int x,int y)   // 从k中分离出[x,y]区间并返回新结点编号,空间复杂度O(2logn)
{
    int n = ++cnt;
    if(x<=l&&y>=r)      // 如果k结点维护的区间在[x,y]中
    {
        sgt[n] = sgt[k];    // 直接拿过来便是
        k = 0;              // 置为NULL,断掉联系
    }
    else 
    {
        int m = (l+r)>>1;
        if(x<=m) sgt[n].l = split(l, m, sgt[k].l, x, y);        // 若左子树中有区间信息
        if(y>m)  sgt[n].r = split(m+1, r, sgt[k].r, x, y);      // 若右子树中有区间信息
        pushup(k);
        pushup(n);      // 更改后记得更新值
    }
    return n;
}
ll query(int l,int r,int k,int x,int y)     // 区间查询
{
    if(x<=l&&y>=r) return sgt[k].val;
    int m = (l+r)>>1;
    ll sum = 0;
    if(x<=m) sum += query(l,m,sgt[k].l,x,y);
    if(y>m)  sum += query(m+1,r,sgt[k].r,x,y);
    return sum;
}
ll query(int l,int r,int k,int kth)         // 单点查询
{
    if(l==r) return l;
    int m = (l+r)>>1;
    if(kth<=sgt[sgt[k].l].val) return query(l,m,sgt[k].l,kth);
    else return query(m+1,r,sgt[k].r,kth-sgt[sgt[k].l].val);
}
int main()
{
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    {
        int x;
        cin>>x;
        modify(1,n,root[1],i,x);            // 初始状态下值都为0,所以加x即为置为x
    }
    int last = 1;
    while(m--)
    {
        int opt,x,y,z;
        cin>>opt>>x>>y;
        switch(opt)
        {
        case 0:
            cin>>z;
            root[++last] = split(1, n, root[x], y, z);
            break;
        case 1:
            merge(root[x],root[y]);
            break;
        case 2:
            cin>>z;
            modify(1,n,root[x],z,y);
            break;
        case 3:
            cin>>z;
            cout<<query(1,n,root[x],y,z)<<endl;
            break;
        case 4:
            if(y>sgt[root[x]].val) cout<<-1<<endl;      // 若只有4个元素却来查询第5大元素(例如),那么结果即为-1
            else cout<<query(1,n,root[x],y)<<endl;
            break;
        }
    }
    return 0;
}

标签:return,val,int,线段,sgt,分裂,root,ll
From: https://www.cnblogs.com/sheepcsy/p/16871025.html

相关文章

  • P3834 【模板】可持久化线段树 2
    先用结构体实现了下,发现写错了(只有20分),后面直接用的数组了  #include<bits/stdc++.h>usingnamespacestd;constintN=2e5+7;inttl[N<<6],tr[N<<6],sum......
  • 线段树(Segment Tree)是一个基于分治的数据结构。
    线段树(SegmentTree)是一个基于分治的数据结构。线段树杂谈 概念:线段树(SegmentTree)是一个基于分治的数据结构。通常处理区间,序列中的查询,更改问题。大体上有单修,单......
  • CF1045G AI Robots (动态开点线段树 + 离散化)(关于其空间复杂度的分析)
    题目大意:火星上有\(N\)个机器人排成一行,第\(i\)个机器人的位置为\(x_i\),视野维\(r_i\),智商为\(q_i\)。我们认为第\(i\)个机器人可以看到的位置是\([x_i-r_i,......
  • 可持久化线段树
    数组一般开maxn<<5,但有的时候也会不够,不知道怎么判断得到的建议是“贴着内存开”。最套路的应用就是各种形式的区间k小:K小数保存一下模板code#include<bits/stdc+......
  • 线段树
    题目链接 题目AC代码:#include<bits/stdc++.h>#defineintlonglongusingnamespacestd;constintN=2e5+10;intm,p;structNode{intl,r;......
  • 线段树动态开点
    点击查看代码#include<bits/stdc++.h>#defineIOSios::sync_with_stdio(false);#defineendl'\n'usingnamespacestd;typedeflonglongll;constintMAXN=......
  • 【luogu P1600】天天爱跑步(线段树合并)(LCA)
    天天爱跑步题目链接:luoguP1600题目大意有一棵树,给你若干条路径,对于每个点,有一个数x,求出有多少条路径的第x个点是当前点。思路考虑把路径拆成两个部分,向上和向下。......
  • 线段树入门
    是一种二叉搜索树,通过二分法访问或修改区间值,区间大小不超过10^6,区间值必须满足区间加法,即仅当对于区间[L,R]的问题的答案可以由[L,M]和[M+1,R]的答案......
  • L - Intersection and Union Gym - 103993L (线段树)
    题意思路思路很巧妙,首先是枚举每个值的贡献,然后找到了规律,下次做题的时候线分析每个题有啥好规律,然后根据规律做题。再就是线段树的这个思路,感觉很巧妙,通过设置每一段的......
  • 可持久化线段树-主席树
    求第k小数#include<bits/stdc++.h>usingnamespacestd;constintN=1e5+10;constintM=1e4+10;vector<int>nums;intn,m;inta[N];introot[N];intidx;struc......