09线段树算法练习题解析

线段树算法练习题解析

01线段树

问题描述

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

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

给你一个数列和一个操作队列,请输出数列经过这些操作队列中所有操作后的结果。

  • 1 <= n,m <= 10^5
  • 数列中所有元素之和 <= 10^18

输入描述

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

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

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

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

输出描述

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

输入样例

5 5

1 5 4 2 3

2 2 4

1 2 3 2

2 3 4

1 1 5 1

2 1 4

输出样例

11

8

20

参考代码

#include

using namespace std;

const int maxn=100010;

int a[maxn+2];

struct tree{
    int l,r;
    long long pre,add;
}t[4*maxn+2];

void bulid(int p,int l,int r){
    t[p].l=l;t[p].r=r;
    if(l==r){
        t[p].pre=a[l];
        return;
    }
    int mid=l+r>>1;
    bulid(p*2,l,mid);
    bulid(p*2+1,mid+1,r);
    t[p].pre=t[p*2].pre+t[p*2+1].pre;
} 

void spread(int p){
    if(t[p].add){
        t[p*2].pre+=t[p].add*(t[p*2].r-t[p*2].l+1);
        t[p*2+1].pre+=t[p].add*(t[p*2+1].r-t[p*2+1].l+1);
        t[p*2].add+=t[p].add;
        t[p*2+1].add+=t[p].add;
        t[p].add=0;
    }
}

void change(int p,int x,int y,int z){
    if(x<=t[p].l && y>=t[p].r){
        t[p].pre+=(long long)z*(t[p].r-t[p].l+1);
        t[p].add+=z;
        return;
    }
    spread(p);
    int mid=t[p].l+t[p].r>>1;
    if(x<=mid) change(p*2,x,y,z);
    if(y>mid) change(p*2+1,x,y,z);
    t[p].pre=t[p*2].pre+t[p*2+1].pre;   
}

long long ask(int p,int x,int y){
    if(x<=t[p].l && y>=t[p].r) return t[p].pre;
    spread(p);
    int mid=t[p].l+t[p].r>>1;
    long long ans=0;
    if(x<=mid) ans+=ask(p*2,x,y);
    if(y>mid) ans+=ask(p*2+1,x,y);
    return ans;
}

int main(){
    int n,m;
    cin>>n>>m;
    for(int i=1;i<=n;i++)
    	cin>>a[i];
    bulid(1,1,n);
    for(int i=1;i<=m;i++)
    {
        int q,x,y,z;
        cin>>q;
        if(q==1){
            cin>>x>>y>>z;
            change(1,x,y,z);
        }
        else {
            cin>>x>>y;
            cout<

02日程安排表

问题描述

给你一个待安排日程表calendar,逐个将日程添加到你的日程安排表中。如果要添加的日程安排不会造成重复预订,则可以存储这个新的日程安排。如果一个日程可以被添加到你的日程安排表中,输出true;否则输出false

当两个日程安排有一些时间上的交叉时(例如两个日程安排都在同一时间内),就会产生 重复预订 。

日程可以用一对整数 startend 表示,这里的时间是半开区间,即 [start, end), 实数 x 的范围为, start <= x < end

  • 0 <= start < end <= 10^9
  • 0 < calendar.length <= 10^9

输入描述

第一行输入一个整数表示 calendar.length

随后calendar.length行,每行两个数分别表示 start_i、end_i

输出描述

输出 calendar.length 行,第i行表示第i个日程是否可以被添加到你的日程安排表中

输入样例

3

10 20

15 25

20 30

输出样例

true

false

true

参考代码

#include
#include

using namespace std;

unordered_map tree, lazy;

bool query(int start, int end, int l, int r, int idx) {
    if (r < start || end < l) {
        return false;
    }
    // 如果该区间已被预订,则直接返回
    if (lazy.count(idx)) {
        return true;
    }
    if (start <= l && r <= end) {
        return tree.count(idx);
    }
    int mid = (l + r) >> 1;
    return query(start, end, l, mid, 2 * idx) ||
           query(start, end, mid + 1, r, 2 * idx + 1);
}

void update(int start, int end, int l, int r, int idx) {
    if (r < start || end < l) {
        return;
    }
    if (start <= l && r <= end) {
        tree[idx]=true;
        lazy[idx]=true;
    } else {
        int mid = (l + r) >> 1;
        update(start, end, l, mid, 2 * idx);
        update(start, end, mid + 1, r, 2 * idx + 1);
        tree[idx]=true;
        if (lazy.count(2 * idx) && lazy.count(2 * idx + 1)) {
            lazy[idx]=true;
        }
    }
}

bool book(int start, int end) {
    if (query(start, end - 1, 0, 1e9, 1)) {
        return false;
    }
    update(start, end - 1, 0, 1e9, 1);
    return true;
}
    
int main(){
	int n, start, end;
	cin>>n;
	for(int i=0;i>start>>end;
		if(book(start, end)){
			cout<<"true"<

你可能感兴趣的:(蓝桥杯,算法,c++,图论,蓝桥杯)