• 线段树入门+例题详解


    线段树是非常经典的树形数据结构,其在ACM中也是经常出现的,下面对线段树进行说明并就相关例题展开。



    问题与解决方法

    如果我们有 n(n>1e5) 个数组成的数组,我们有 q(q>1e5) 查询,每次查询 [li,ri] 范围内的数之和,可以使用前缀和来解决,这样每次查询的时间复杂度都是O(1)。

    还是有 n(n>1e5) 个数组成的数组,我们有 u(u>1e5) 次修改,每次修改 [li,ri] 范围内的数,每个数加上一个 numberi,问 u 次修改后某个数是多少,可以使用差分来解决,这样每次修改的时间复杂度都是O(1)。

    单点修改区间查询

    现在有一个问题,还是有一个由 n(n>1e5) 个数组成的数组,我们有 q(q>1e5) 操作,但每次操作操作有两种可能:

    • 一是对数组中的一个数的值进行修改
    • 二是查询 [li,ri] 范围的数之和

    这样用之前前缀和的方法最大时间复杂度为 O(nq),因为每次修改的时间复杂度都是 O(n)。

    而线段树则可以在可接受的时间复杂度内解决这个问题,下面为其树形结构图:

    这里面每个 mid 表示 [l,r] 的中间值,即 (l+r)/2,比如 mid1=(l+r)/2,mid2=(1+mid1)/2…,每个区间每次分为两个均匀的区间,直到每个区间只有一个数,这里的对于每个树中的 [l,r] 维护这个范围的数之和。

    这个树形结构的树深度为 log2(n),那么每次对数组中的一个数的只需要寻找 log2(n) 次,即时间复杂度为 O(logn)。

    而区间查询的时间复杂度也是 O(logn),我们可以考虑两种情况:

    • 查询范围只包含一个子区间,那么也是最多 log2(n) 次查找
    • 查询返回包含两个子区间,那么对于再下一层的区间,只有两种情况,一是又包含两个子区间,但由于上一层包含了右子区间,那么当前层的右区间是可以直接返回的,继续向左区间下层搜索即可。二是只包含一个子区间,那么只需从包含的那个子区间向下继续搜索即可,时间复杂度仍是 O(logn)。

      上面的论证并不详细,具体看代码部分。

    定义线段树的结构体

    const int N = 4e5+5; //总节点数
    struct node
    {
        int l/*区间左边界*/,r/*区间右边界*/,sum/*区间元素之和*/;
    }tree[N];
    

    更新节点k的sum

    void update(int k) // 更新节点k的sum;
    {
        tree[k].sum=tree[k<<1].sum+tree[k<<1|1].sum;
        //一段区间的元素和等于它的子区间的元素和
    }
    

    因为我们每次修改一个元素的值,必定会对其上层的节点的 sum 产生影响,所以每次要重新计算一下节点 ksum

    初始化线段树

    void build(int k/*当前节点的编号*/,int l/*当前区间的左边界*/,int r/*当前区间的右边界*/) //初始化线段树
    {
    	tree[k].l=l,tree[k].r=r;
    	if(l==r)//递归到叶节点
    	{
    		tree[k].sum=number[l];//其中number数组为给定的初值
    		return;
    	}
    	int mid=(l+r)>>1;//计算左右子节点的边界
    	build(k<<1,l,mid);//递归进左儿子[l,mid]
    	build(k<<1|1,mid+1,r);//递归进右儿子[mid+1,r]
    	update(k);//最后要用左右子区间的值更新该区间的值
    }
    

    [l,r]lr 相等时,表示递归了叶节点了,要返回了,当前节点的 sum=number[l]

    单点修改

    void change(int k/*当前节点的编号*/,int x/*要修改节点的编号*/,int y/*要把编号为x的数字修改成y*/) // 单点修改
    {
        //如果当前区间只包含一个元素,那么该元素一定就是我们要修改的。
    	//由于该区间的sum一定等于编号为x的数字,所以直接修改sum就可以了。
    	if(tree[k].l==tree[k].r)
        {
            tree[k].sum=y;
            return;
        }
    	int mid=(tree[k].l+tree[k].r)>>1;//计算下一层子区间的左右边界
    	if(x<=mid)
            change(k<<1,x,y);//递归进左儿子
    	else
            change(k<<1|1,x,y);//递归进右儿子
    	update(k);//最后更新点k的值
    }
    

    区间查询

    int query(int k/*当前节点的编号*/,int l/*当前查询区间的左边界*/,int r/*当前查询区间的右边界*/) // 区间查询
    //当前到了编号为k的节点,查询[l,r]范围的和
    {
        //如果当前区间是询问区间的子区间,可以直接返回
    	if(tree[k].l>=l&&tree[k].r<=r)
            return tree[k].sum;
    	int mid=(tree[k].l+tree[k].r)>>1; //计算下一层子区间的左右边界
        int res=0;
        //如果询问区间包含左子区间的部分
    	if(l<=mid)
            res += query(k<<1,l,r);
        //如果询问区间包含右子区间的部分
    	if(r>mid)
            res += query(k<<1|1,l,r);
    	return res;
    }
    

    区间修改区间查询

    区间修改与单点修改不同的是其每次是对一个区间[li,ri]进行修改。如果按照单点修改的写法,那么每次都更新到叶节点,那么时间复杂度最大为 O(nlogn),即 n 节点都递归 logn 层。这样的时间复杂度是不可接受的。

    这时可以引入一个叫懒标记(lazy)的东西,我们每次区间,向下更新时,如果要更新的区间包含当前节点的区间,就不向下更新了,而是维护一个 lazy,把要修改的值更新入 lazy 中。之后当需要进入当前节点下面节点时,再将 lazy 下传下去,去更新下面节点的区间和以及 lazy,再将当前节点清零。

    其实可以发现这和区间查询的方式很类似,所以其时间复杂度也为 O(logn)。

    定义线段树的结构

    const int N = 4e5+5; //总节点数
    struct node
    {
        int l/*区间左边界*/,r/*区间右边界*/,sum/*区间元素之和*/,lazy/*懒惰标记*/;
    }tree[N];
    

    相比于单点修改多了 lazy 懒标记。

    懒标记下传

    void pushdown(int k) //懒标记下传
    {
        tree[k<<1].lazy += tree[k].lazy;
        tree[k<<1|1].lazy += tree[k].lazy;
        tree[k<<1].sum += tree[k].lazy * (tree[k<<1].r - tree[k<<1].l + 1); //左节点要加上懒标记的值*(左节点区间长)
        tree[k<<1|1].sum += tree[k].lazy * (tree[k<<1|1].r - tree[k<<1|1].l + 1); //右节点要加上懒标记的值*(右节点区间长)
        tree[k].lazy = 0; //下传到子节点之和当前点的懒标记要清零
    }
    

    先更新子节点的懒标记,再更新子节点的元素和,最后对懒标记清零。

    更新节点k的sum

    void update(int k) // 更新节点k的sum;
    {
        tree[k].sum=tree[k<<1].sum+tree[k<<1|1].sum;
        //一段区间的元素和等于它的子区间的元素和
    }
    

    初始化线段树

    void build(int k/*当前节点的编号*/,int l/*当前区间的左边界*/,int r/*当前区间的右边界*/) //初始化线段树
    {
    	tree[k].l=l,tree[k].r=r;
    	lazy[k]=0; // 初始懒标记
    	if(l==r)//递归到叶节点
    	{
    		tree[k].sum=number[l];//其中number数组为给定的初值
    		return;
    	}
    	int mid=(l+r)>>1;//计算左右子节点的边界
    	build(k<<1,l,mid);//递归进左儿子[l,mid]
    	build(k<<1|1,mid+1,r);//递归进右儿子[mid+1,r]
    	update(k);//最后要用左右子区间的值更新该区间的值
    }
    

    区间修改

    void add(int k/*当前节点的编号*/, int l/*当前更新区间的左边界*/, int r/*当前更新区间的右边界*/, int val/*区间每个元素增加的值*/) // 区间更新
    {
        //如果当前区间是更新区间的子区间,更新sum与lazy
        if(tree[k].l>=l&&tree[k].r<=r)
        {
            tree[k].sum += (tree[k].r - tree[k].l + 1)*val;
            tree[k].lazy += val;
            return ;
        }
        //如果当前节点被打上了懒惰标记,那么就把这个标记下传
        if(tree[k].lazy)
            pushdown(k);
        int mid=(tree[k].l+tree[k].r)>>1;//计算下一层子区间的左右边界
        //如果更新区间包含左子区间的部分
        if(l<=mid)
            add(k<<1,l,r,val);
        if(r>mid) //如果更新区间包含右子区间的部分
            add(k<<1|1,l,r,val);
        update(k);//最后更新点k的值
    }
    

    区间查询

    int query(int k/*当前节点的编号*/,int l/*当前查询区间的左边界*/,int r/*当前查询区间的右边界*/) // 区间查询
    //当前到了编号为k的节点,查询[l,r]范围的和
    {
        //如果当前区间是询问区间的子区间,可以直接返回
    	if(tree[k].l>=l&&tree[k].r<=r)
            return tree[k].sum;
        //如果当前节点被打上了懒惰标记,那么就把这个标记下传
    	if(tree[k].lazy)
            pushdown(k);
    	int mid=(tree[k].l+tree[k].r)>>1; //计算下一层子区间的左右边界
        int res=0;
        //如果询问区间包含左子区间的部分
    	if(l<=mid)
            res += query(k<<1,l,r);
        //如果询问区间包含右子区间的部分
    	if(r>mid)
            res += query(k<<1|1,l,r);
    	return res;
    }
    

    例题与解析

    【模板】线段树 1

    题目链接

    点我(^_^)

    题目描述

    如题,已知一个数列,你需要进行下面两种操作:

    1. 将某区间每一个数加上 k k k
    2. 求出某区间每一个数的和。

    输入格式

    第一行包含两个整数 n , m n, m n,m,分别表示该数列数字的个数和操作的总个数。

    第二行包含 n n n 个用空格分隔的整数,其中第 i i i 个数字表示数列第 i i i 项的初始值。

    接下来 m m m 行每行包含 3 3 3 4 4 4 个整数,表示一个操作,具体如下:

    1. 1 x y k:将区间 [ x , y ] [x, y] [x,y] 内每个数加上 k k k
    2. 2 x y:输出区间 [ x , y ] [x, y] [x,y] 内每个数的和。

    输出格式

    输出包含若干行整数,即为所有操作 2 的结果。

    样例 #1

    样例输入 #1
    5 5
    1 5 4 2 3
    2 2 4
    1 2 3 2
    2 3 4
    1 1 5 1
    2 1 4
    
    样例输出 #1
    11
    8
    20
    

    提示

    对于 30 % 30\% 30% 的数据: n ≤ 8 n \le 8 n8 m ≤ 10 m \le 10 m10
    对于 70 % 70\% 70% 的数据: n ≤ 10 3 n \le {10}^3 n103 m ≤ 10 4 m \le {10}^4 m104
    对于 100 % 100\% 100% 的数据: 1 ≤ n , m ≤ 10 5 1 \le n, m \le {10}^5 1n,m105

    保证任意时刻数列中所有元素的绝对值之和 ≤ 10 18 \le {10}^{18} 1018

    【样例解释】

    解题思路

    这题就是区间修改与区间查询的模板题,需要注意的是,其元素之和的上限是 1e18,所以需要开 long long

    AC代码(C++)

    #include 
    using namespace std;
    const int N = 4e5+5; //总节点数
    struct node
    {
    //    int l/*区间左边界*/,r/*区间右边界*/,sum/*区间元素之和*/,lazy/*懒惰标记*/;
        int l/*区间左边界*/,r/*区间右边界*/;
        long long sum;
        long long lazy;
    }tree[N];
    long long number[N];
    void pushdown(int k) //懒标记下传
    {
        tree[k<<1].lazy += tree[k].lazy;
        tree[k<<1|1].lazy += tree[k].lazy;
        tree[k<<1].sum += tree[k].lazy * (tree[k<<1].r - tree[k<<1].l + 1); //左节点要加上懒标记的值*(左节点区间长)
        tree[k<<1|1].sum += tree[k].lazy * (tree[k<<1|1].r - tree[k<<1|1].l + 1); //右节点要加上懒标记的值*(右节点区间长)
        tree[k].lazy = 0; //下传到子节点之和当前点的懒标记要清零
    }
    void update(int k) // 更新节点k的sum;
    {
        tree[k].sum=tree[k<<1].sum+tree[k<<1|1].sum;
        //一段区间的元素和等于它的子区间的元素和
    }
    void build(int k/*当前节点的编号*/,int l/*当前区间的左边界*/,int r/*当前区间的右边界*/) //初始化线段树
    {
    	tree[k].l=l,tree[k].r=r;
    	if(l==r)//递归到叶节点
    	{
    		tree[k].sum=number[l];//其中number数组为给定的初值
    		return;
    	}
    	int mid=(l+r)>>1;//计算左右子节点的边界
    	build(k<<1,l,mid);//递归进左儿子[l,mid]
    	build(k<<1|1,mid+1,r);//递归进右儿子[mid+1,r]
    	update(k);//最后要用左右子区间的值更新该区间的值
    }
    void add(int k/*当前节点的编号*/, int l/*当前更新区间的左边界*/, int r/*当前更新区间的右边界*/, long long val/*区间每个元素增加的值*/) // 区间更新
    {
        //如果当前区间是更新区间的子区间,更新sum与lazy
        if(tree[k].l>=l&&tree[k].r<=r)
        {
            tree[k].sum += (tree[k].r - tree[k].l + 1)*val;
            tree[k].lazy += val;
            return ;
        }
        //如果当前节点被打上了懒惰标记,那么就把这个标记下传
        if(tree[k].lazy)
            pushdown(k);
        int mid=(tree[k].l+tree[k].r)>>1;//计算下一层子区间的左右边界
        //如果更新区间包含左子区间的部分
        if(l<=mid)
            add(k<<1,l,r,val);
        if(r>mid) //如果更新区间包含右子区间的部分
            add(k<<1|1,l,r,val);
        update(k);//最后更新点k的值
    }
    long long query(int k/*当前节点的编号*/,int l/*当前查询区间的左边界*/,int r/*当前查询区间的右边界*/) // 区间查询
    //当前到了编号为k的节点,查询[l,r]范围的和
    {
        //如果当前区间是询问区间的子区间,可以直接返回
    	if(tree[k].l>=l&&tree[k].r<=r)
            return tree[k].sum;
        //如果当前节点被打上了懒惰标记,那么就把这个标记下传
    	if(tree[k].lazy)
            pushdown(k);
    	int mid=(tree[k].l+tree[k].r)>>1; //计算下一层子区间的左右边界
        long long res=0;
        //如果询问区间包含左子区间的部分
    	if(l<=mid)
            res += query(k<<1,l,r);
        //如果询问区间包含右子区间的部分
    	if(r>mid)
            res += query(k<<1|1,l,r);
    	return res;
    }
    
    int main()
    {
        int n,m;
        cin>>n>>m;
        for(int i=1; i<=n; ++i)
            cin>>number[i];
        build(1,1,n);
        while(m--)
        {
            int opt;
            cin>>opt;
            if(opt == 1)
            {
                int x,y;
                long long z;
                cin>>x>>y>>z;
                add(1,x,y,z);
            }
            else
            {
                int x,y;
                cin>>x>>y;
                cout<<query(1,x,y)<<endl;
            }
        }
        return 0;
    }
    

    【模板】线段树 2

    题目链接

    点我(^_^)

    题目描述

    如题,已知一个数列,你需要进行下面三种操作:

    • 将某区间每一个数乘上 x x x

    • 将某区间每一个数加上 x x x

    • 求出某区间每一个数的和

    输入格式

    第一行包含三个整数 n , m , p n,m,p n,m,p,分别表示该数列数字的个数、操作的总个数和模数。

    第二行包含 n n n 个用空格分隔的整数,其中第 i i i 个数字表示数列第 i i i 项的初始值。

    接下来 m m m 行每行包含若干个整数,表示一个操作,具体如下:

    操作 1 1 1: 格式:1 x y k 含义:将区间 [ x , y ] [x,y] [x,y] 内每个数乘上 k k k

    操作 2 2 2: 格式:2 x y k 含义:将区间 [ x , y ] [x,y] [x,y] 内每个数加上 k k k

    操作 3 3 3: 格式:3 x y 含义:输出区间 [ x , y ] [x,y] [x,y] 内每个数的和对 p p p 取模所得的结果

    输出格式

    输出包含若干行整数,即为所有操作 3 3 3 的结果。

    样例 #1

    样例输入 #1
    5 5 38
    1 5 4 2 3
    2 1 4 1
    3 2 5
    1 2 4 2
    2 3 5 5
    3 1 4
    
    样例输出 #1
    17
    2
    

    提示

    【数据范围】

    对于 30 % 30\% 30% 的数据: n ≤ 8 n \le 8 n8 m ≤ 10 m \le 10 m10
    对于 70 % 70\% 70% 的数据: n ≤ 1 0 3 n \le 10^3 n103 m ≤ 1 0 4 m \le 10^4 m104
    对于 100 % 100\% 100% 的数据: n ≤ 1 0 5 n \le 10^5 n105 m ≤ 1 0 5 m \le 10^5 m105

    除样例外, p = 571373 p = 571373 p=571373

    (数据已经过加强_

    【样例说明】

    故输出应为 17 17 17 2 2 2 40   m o d   38 = 2 40 \bmod 38 = 2 40mod38=2

    解题思路

    主要探究懒标记的更新及下传,这里假定当前区间的和为 sum,区间范围为 [l,r],下传下来的懒标记(即上一层的懒标记)为加法懒标记lazyk1,减法懒标记lazyk2,当前层的加法加法懒标记为 lazykk1 和 乘法懒标记 lazykk2, 因为子区间的两个区间更新方法相同,所以这里就只考虑一种。

    那么对于加法,还是和上一题一样。
    更新加法

    sum = sum + (r-l+1)*val;
    lazykk1 = lazykk1+val;
    

    更新乘法
    因为是每个数乘上一个数val,所以每个数乘val之和相当于每个数之和乘val
    lazykk1 要乘上 val,这是因为 lazykk1*(r-l+1)*val = (lazykk1*val)*(r-l+1)

    sum = sum*val
    lazykk1 = lazykk1*val;
    lazykk2 = lazykk2*val;
    

    懒标记的下传

    lazykk1 = lazykk1*lazyk2+lazykk1;
    lazykk2 = lazykk2*lazyk2
    sum = sum*lazyk2+lazyk1*(r-l+1);
    lazyk1 = 0;
    lazyk2 = 1; 
    

    因为乘法对加法的影响已经在懒标记计算时进行过了,所以这里只需要乘上 sum 就可以了。注意 lazyk2 要初始化为 1,已经注意取模计算。

    AC代码(C++)

    #include 
    using namespace std;
    const int N = 4e5+10; //总节点数
    long long mod;
    struct node
    {
        int l/*区间左边界*/,r/*区间右边界*/;
        long long lazy1/*加法懒惰标记*/,lazy2/*乘法懒惰标记*/,sum/*区间元素之和*/;
    
    }tree[N];
    long long number[N];
    void pushdown(int k) //懒标记下传
    {
        tree[k<<1].lazy1 = (tree[k<<1].lazy1*tree[k].lazy2+tree[k].lazy1)%mod;
        tree[k<<1|1].lazy1 = (tree[k<<1|1].lazy1*tree[k].lazy2+tree[k].lazy1)%mod;
        tree[k<<1].lazy2 = tree[k<<1].lazy2*tree[k].lazy2%mod;
        tree[k<<1|1].lazy2 = tree[k<<1|1].lazy2*tree[k].lazy2%mod;
        tree[k<<1].sum = tree[k<<1].sum*tree[k].lazy2%mod; //先乘上lazy2
        tree[k<<1|1].sum = tree[k<<1|1].sum*tree[k].lazy2%mod;
        tree[k<<1].sum = (tree[k<<1].sum + tree[k].lazy1*(tree[k<<1].r-tree[k<<1].l+1))%mod; //再加上区间长度*lazy1
        tree[k<<1|1].sum = (tree[k<<1|1].sum + tree[k].lazy1*(tree[k<<1|1].r-tree[k<<1|1].l+1))%mod;
        tree[k].lazy1 = 0; //下传到子节点之和当前点的懒标记要清零
        tree[k].lazy2 = 1;
    }
    void update(int k) // 更新节点k的sum;
    {
        tree[k].sum=(tree[k<<1].sum+tree[k<<1|1].sum)%mod;
        //一段区间的元素和等于它的子区间的元素和
    }
    void build(int k/*当前节点的编号*/,int l/*当前区间的左边界*/,int r/*当前区间的右边界*/) //初始化线段树
    {
    	tree[k].l=l,tree[k].r=r;
    	tree[k].lazy2 = 1;
    	tree[k].lazy1 = 0;
    	if(l==r)//递归到叶节点
    	{
    		tree[k].sum=number[l]%mod;//其中number数组为给定的初值
    		return;
    	}
    	int mid=(l+r)>>1;//计算左右子节点的边界
    	build(k<<1,l,mid);//递归进左儿子[l,mid]
    	build(k<<1|1,mid+1,r);//递归进右儿子[mid+1,r]
    	update(k);//最后要用左右子区间的值更新该区间的值
    }
    
    void add(int k/*当前节点的编号*/, int l/*当前更新区间的左边界*/, int r/*当前更新区间的右边界*/, long long val/*区间每个元素增加的值*/) // 区间更新
    {
        //如果当前区间是更新区间的子区间,更新sum与lazy
        if(tree[k].l>=l&&tree[k].r<=r)
        {
            tree[k].sum = (tree[k].sum+(tree[k].r - tree[k].l + 1)*val)%mod;
            tree[k].lazy1 = (tree[k].lazy1+val)%mod;
            return ;
        }
        //如果当前节点被打上了懒惰标记,那么就把这个标记下传
        if(tree[k].lazy1 || tree[k].lazy2!=1)
            pushdown(k);
        int mid=(tree[k].l+tree[k].r)>>1;//计算下一层子区间的左右边界
        //如果更新区间包含左子区间的部分
        if(l<=mid)
            add(k<<1,l,r,val);
        if(r>mid) //如果更新区间包含右子区间的部分
            add(k<<1|1,l,r,val);
        update(k);//最后更新点k的值
    }
    
    void mul(int k/*当前节点的编号*/, int l/*当前更新区间的左边界*/, int r/*当前更新区间的右边界*/, long long val/*区间每个元素要乘的值*/) // 区间更新
    {
        //如果当前区间是更新区间的子区间,更新sum与lazy
        if(tree[k].l>=l&&tree[k].r<=r)
        {
            tree[k].sum = tree[k].sum*val%mod;
            tree[k].lazy1 = tree[k].lazy1*val%mod;
            tree[k].lazy2 = tree[k].lazy2*val%mod;
            return ;
        }
        //如果当前节点被打上了懒惰标记,那么就把这个标记下传
        if(tree[k].lazy1 || tree[k].lazy2!=1)
            pushdown(k);
        int mid=(tree[k].l+tree[k].r)>>1;//计算下一层子区间的左右边界
        //如果更新区间包含左子区间的部分
        if(l<=mid)
            mul(k<<1,l,r,val);
        if(r>mid) //如果更新区间包含右子区间的部分
            mul(k<<1|1,l,r,val);
        update(k);//最后更新点k的值
    }
    
    long long query(int k/*当前节点的编号*/,int l/*当前查询区间的左边界*/,int r/*当前查询区间的右边界*/) // 区间查询
    //当前到了编号为k的节点,查询[l,r]范围的和
    {
        //如果当前区间是询问区间的子区间,可以直接返回
    //    if(tree[k].l>=l&&tree[k].r<=r)
    //     {
    //         cout<
    //     }
    	if(tree[k].l>=l&&tree[k].r<=r)
            return tree[k].sum%mod;
        //如果当前节点被打上了懒惰标记,那么就把这个标记下传
    	if(tree[k].lazy1 || tree[k].lazy2!=1)
            pushdown(k);
    	int mid=(tree[k].l+tree[k].r)>>1; //计算下一层子区间的左右边界
        long long res=0;
        //如果询问区间包含左子区间的部分
    	if(l<=mid)
            res = (res + query(k<<1,l,r))%mod;
        //如果询问区间包含右子区间的部分
    	if(r>mid)
            res = (res + query(k<<1|1,l,r))%mod;
    	return res%mod;
    }
    
    int main()
    {
        int n,m;
        cin>>n>>m>>mod;
        for(int i=1; i<=n; ++i)
            cin>>number[i];
        build(1,1,n);
        while(m--)
        {
            int opt;
            cin>>opt;
            if(opt == 1)
            {
                int x,y;
                long long z;
                cin>>x>>y>>z;
                mul(1,x,y,z);
            }
            else if(opt == 2)
            {
                int x,y;
                long long z;
                cin>>x>>y>>z;
                add(1,x,y,z);
            }
            else
            {
                int x,y;
                cin>>x>>y;
                cout<<query(1,x,y)<<endl;
            }
        }
        return 0;
    }
    
    
  • 相关阅读:
    本周二晚19:00战码先锋第8期直播丨如何多方位参与OpenHarmony开源贡献
    诚迈科技旗下智达诚远亮相2023世界新汽车技术合作生态展
    ESP32设备驱动-Nokia5110显示屏驱动
    SCXI-1104C NI 利用人工智能和机器学习进行网络防御
    罗永浩讽刺iPhone“那么伟大又那么不要脸”;北欧囚犯正在训练AI大模型;ChatGPT治怪病丨RTE开发者日报 Vol.51
    MySQL---索引+事务
    SpringBoot 学习(八)异步任务,邮件发送和定时执行
    收录一些常见的算法题型
    <C++> 模拟实现string
    【图形学】26 透明效果基础
  • 原文地址:https://blog.csdn.net/weixin_44491423/article/details/126949965