线段树的例题

影子的宽度

题目描述

桌子上零散地放着若干个盒子,盒子都平行于墙。桌子的后方是一堵墙。如图所示。现在从桌子的前方射来一束平行光, 把盒子的影子投射到了墙上。问影子的总宽度是多少?

输入

第1行:3个整数L,R,N。-100000 <=L<=R<= 100000,表示墙所在的区间;1<=N<=100000,表示盒子的个数

接下来N行,每行2个整数BL, BR,-100000 <=BL<=BR<= 100000,表示一个盒子的左、右端点(左闭右开)

输出

第1行:1个整数W,表示影子的总宽度。

样例输入

Sample Input 1

0 7 2
1 2
4 5

Sample Output 1
2

Sample Input 2

-10 10 2
-5 2
-2 2

Sample Output 2
7

Sample Input 3

-10 10 3
-7 0
-4 9
-4 2

Sample Output 3
16

Sample Input 4

-100 100 3
-7 2
5 9
2 5

Sample Output 4
16

Sample Input 5

-50 50 4
-2 4
0 6
9 10
-5 30

Sample Output 5
35

大致就是一道考察懒标记的题,比较简单

因为下标是可能为负,需要加一个偏移量

#include <bits/stdc++.h>
using namespace std;
const int MOD = 1e9 + 7;
const int N = 1e5 + 10;
typedef long long ll;

ll a[N];

struct Node {
    int l, r;
    ll sum, maxv, lazy;
    void updade() {
        sum = r - l + 1;
        lazy = 1;
    }
} tree[N * 4];

void build(int p, int l, int r)
{
    tree[p].l = l, tree[p].r = r;
    tree[p].sum = tree[p].lazy = 0;
    if (l == r) {
        tree[p].sum = tree[p].maxv = a[l];
        return;
    }
    int mid = l + r >> 1;
    build(p << 1, l , mid);
    build(p << 1 | 1, mid + 1, r);
    tree[p].sum = tree[p << 1].sum + tree[p << 1 | 1].sum;
    tree[p].maxv = max(tree[p << 1].maxv, tree[p << 1 | 1].maxv);
}

void spread(int p)
{
    int lazy_val = tree[p].lazy;
    if (lazy_val) {
        tree[p << 1].updade();
        tree[p << 1 | 1].updade();
        tree[p].lazy = 0;
    }
}

void update(int p, int l, int r)
{
    if (l <= tree[p].l &&  tree[p].r <= r) {
        tree[p].updade();
    } else {
        spread(p);
        int mid = tree[p].l + tree[p].r  >> 1;
        if (l <= mid) 
			update(p << 1, l, r);
        if (r > mid) 
			update(p << 1 | 1, l, r);
        tree[p].sum = tree[p << 1].sum + tree[p << 1 | 1].sum;
        //tree[p].maxv = max(tree[p << 1].maxv , tree[p << 1 | 1].maxv);
    }
}

ll query(int p, int l, int r)
{
    if (l <= tree[p].l && tree[p].r <= r) 
		return tree[p].sum;

    spread(p);
    ll ans = 0;
    int mid = tree[p].l + tree[p].r >> 1;
    if (l <= mid) 
		ans = ans + query(p << 1, l, r);
    if (r > mid) 
		ans = ans + query(p << 1 | 1, l, r);
    return ans;
}

int main()
{
    int L, R, n, q, l, r, d;
    scanf("%d%d%d", &L, &R, &q);
    d = -L + 1; // 偏移量 
    build(1, L + d, R + d);
    while (q--) { 
        scanf("%d%d", &l, &r);
        
        update(1, l + d, r - 1 + d);
    }
    printf("%lld\n", query(1, L + d, R + d));
    
    return 0;
}

区间平方和问题:

给你一个长度为N的序列,之后有M次操作,操作包括两类:

A L R 为给区间[L,R]的所有数都加上一个A的值

S L R 为求区间[L,R]的所有数的平方的和

如果一个区间内的所有数字,都加上A,那么psum = 2 * sum * A + (r - l + 1) * A
注意要先更新psum,再更新sum

#include <bits/stdc++.h>
using namespace std;
const int MOD = 1e9 + 7;
const int N = 1e5 + 10;
typedef long long ll;

ll a[N];

struct Node {
    int l, r;
    ll sum, psum, maxv, lazy;
    void updade(ll x) {
        psum += (r - l + 1) * x * x + 2 * x * sum;
        sum += (r - l + 1) * x;
        lazy += x;
    }
} tree[N * 4];

void push_up(int p)
{
	tree[p].sum = tree[p << 1].sum + tree[p << 1 | 1].sum;
    tree[p].psum = tree[p << 1].psum + tree[p << 1 | 1].psum;
}

void build(int p, int l, int r)
{
    tree[p].l = l, tree[p].r = r;
    tree[p].psum = tree[p].sum = tree[p].lazy = 0;
    if (l == r) {
        tree[p].sum = tree[p].maxv = a[l];
        tree[p].psum = a[l] * a[l];
        return;
    }
    int mid = l + r >> 1;
    build(p << 1, l , mid);
    build(p << 1 | 1, mid + 1, r);
    push_up(p);
    //tree[p].maxv = max(tree[p << 1].maxv, tree[p << 1 | 1].maxv);
}

void spread(int p)
{
    int lazy_val = tree[p].lazy;
    if (lazy_val) {
        tree[p << 1].updade(lazy_val);
        tree[p << 1 | 1].updade(lazy_val);
        tree[p].lazy = 0;
    }
}

void update(int p, int l, int r, int val)
{
    if (l <= tree[p].l &&  tree[p].r <= r) {
        tree[p].updade(val);
    } else {
        spread(p);
        int mid = tree[p].l + tree[p].r  >> 1;
        if (l <= mid) 
			update(p << 1, l, r, val);
        if (r > mid) 
			update(p << 1 | 1, l, r, val);
        push_up(p);
    }
}

ll query(int p, int l, int r)
{
    if (l <= tree[p].l && tree[p].r <= r) 
		return tree[p].psum;

    spread(p);
    ll ans = 0;
    int mid = tree[p].l + tree[p].r >> 1;
    if (l <= mid) 
		ans = ans + query(p << 1, l, r);
    if (r > mid) 
		ans = ans + query(p << 1 | 1, l, r);
    return ans;
}

int main()
{
    int n, q, l, r, val, opt;
    scanf("%d", &n);
    for (int i = 1; i <= n; ++i) 
		scanf("%lld", a + i);
    build(1, 1, n);
    scanf("%d", &q);
    while (q--) {
        scanf("%d", &opt);
        if (opt == 2) {
            scanf("%d %d", &l, &r);
            printf("%lld\n", query(1, l, r));
        } else {
            scanf("%d %d %d", &l, &r, &val);
            update(1, l, r, val);
        }
    }
    return 0;
}

区间开根号问题

还有bug没有调出来QAQ

stars

题目传送门:poj 2352

需要注意线段树的大小,还有因为 x 坐标是从 0 开始的,需要对x++

#include <cstdio>
#include <algorithm>
using namespace std;
const int MOD = 1e9 + 7;
const int N = 3e4 + 10;
typedef long long ll;

ll a[N];
ll res[N];

struct Node {
    int l, r;
    ll sum, maxv, lazy;
    void updade(ll x) {
        sum += (r - l + 1) * x;
        maxv += x;
        lazy += x;
    }
} tree[N * 4];

void build(int p, int l, int r)
{
    tree[p].l = l, tree[p].r = r;
    tree[p].sum = tree[p].lazy = 0;
    if (l == r) {
        tree[p].sum = tree[p].maxv = a[l];
        return;
    }
    int mid = l + r >> 1;
    build(p << 1, l , mid);
    build(p << 1 | 1, mid + 1, r);
    tree[p].sum = tree[p << 1].sum + tree[p << 1 | 1].sum;
    tree[p].maxv = max(tree[p << 1].maxv, tree[p << 1 | 1].maxv);
}

void spread(int p)
{
    int lazy_val = tree[p].lazy;
    if (lazy_val) {
        tree[p << 1].updade(lazy_val);
        tree[p << 1 | 1].updade(lazy_val);
        tree[p].lazy = 0;
    }
}

void update(int p, int x, int val)
{
    if (tree[p].l == tree[p].r) {
        tree[p].updade(val);
    } else {
        //spread(p);
        int mid = tree[p].l + tree[p].r >> 1;
        if (x <= mid) 
			update(p << 1, x, val);
        if (x > mid) 
			update(p << 1 | 1, x, val);
        tree[p].sum = tree[p << 1].sum + tree[p << 1 | 1].sum;
        tree[p].maxv = max(tree[p << 1].maxv , tree[p << 1 | 1].maxv);
    }
}

ll query(int p, int l, int r)
{
    if (l <= tree[p].l && tree[p].r <= r) 
		return tree[p].sum;

    //spread(p);
    ll ans = 0;
    int mid = tree[p].l + tree[p].r >> 1;
    if (l <= mid) 
		ans = ans + query(p << 1, l, r);
    if (r > mid) 
		ans = ans + query(p << 1 | 1, l, r);
    return ans;
}

int main()
{
    int n, q, x, y, val, opt;
    scanf("%d", &n);
    build(1, 1, 32005);
    for (int i = 1; i <= n; i++) {
        scanf("%d%d", &x, &y); 
        x++;
        int k = query(1, 1, x);
        res[k]++;
        update(1, x, 1);
    }
    for (int i = 0; i <= n - 1; i++) {
    	printf("%lld\n", res[i]);
    }
    
    return 0;
}

逆序对

题目传送门:逆序对

需要进行离散化

离散化之后从右向左依次遍历,假设当前数是 x ,查询有多少之前出现过的(即在 x 右边的)数字

#include <bits/stdc++.h>
using namespace std;
const int MOD = 1e9 + 7;
const int N = 5e5 + 10;
typedef long long ll;

ll a[N], b[N];
int n, m;

struct Node {
    int l, r;
    ll sum, maxv, lazy;
    void updade(ll x) {
        sum += (r - l + 1) * x;
        maxv += x;
        lazy += x;
    }
} tree[N * 4];

void push_up(int p)
{
	tree[p].sum = tree[p << 1].sum + tree[p << 1 | 1].sum;
    tree[p].maxv = max(tree[p << 1].maxv, tree[p << 1 | 1].maxv);
}

void build(int p, int l, int r)
{
    tree[p].l = l, tree[p].r = r;
    tree[p].sum = tree[p].lazy = 0;
    if (l == r) {
        tree[p].sum = tree[p].maxv = a[l];
        return;
    }
    int mid = l + r >> 1;
    build(p << 1, l , mid);
    build(p << 1 | 1, mid + 1, r);
    push_up(p);
}

void spread(int p)
{
    int lazy_val = tree[p].lazy;
    if (lazy_val) {
        tree[p << 1].updade(lazy_val);
        tree[p << 1 | 1].updade(lazy_val);
        tree[p].lazy = 0;
    }
}

void update(int p, int l, int r, int val)
{
    if (l <= tree[p].l &&  tree[p].r <= r) {
        tree[p].updade(val);
    } else {
        spread(p);
        int mid = tree[p].l + tree[p].r  >> 1;
        if (l <= mid) 
			update(p << 1, l, r, val);
        if (r > mid) 
			update(p << 1 | 1, l, r, val);
        push_up(p);
    }
}

ll query(int p, int l, int r)
{
    if (l <= tree[p].l && tree[p].r <= r) 
		return tree[p].sum;

    spread(p);
    ll ans = 0;
    int mid = tree[p].l + tree[p].r >> 1;
    if (l <= mid) 
		ans = ans + query(p << 1, l, r);
    if (r > mid) 
		ans = ans + query(p << 1 | 1, l, r);
    return ans;
}

void discrete()
{
	sort(b + 1, b + n + 1);
	for (int i = 1; i <= n; i++) {
		if (i == 1 || b[i] != b[i - 1])
			b[++m] = b[i];
	}
}

int main()
{
    int q, l, r, val, opt;
    scanf("%d", &n);
    build(1, 1, n);
    
    for (int i = 1; i <= n; ++i) {
		scanf("%lld", a + i);
		b[i] = a[i];
	}
	ll idx, res = 0;
    discrete();
    for (int i = n; i >= 1; i--) {
    	idx = lower_bound(b + 1, b + m + 1, a[i]) - b;
        res += query(1, 1, idx - 1);
        update(1, idx, idx, 1);
    }
    printf("%lld\n", res);
    
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值