洛谷题单 206【数据结构2-2】线段树与树状数组

P3372 【模板】线段树 1

区间修改和区间求和

#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(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 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);
        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 += query(p << 1, l, r);
    if (r > mid) 
		ans += query(p << 1 | 1, l, r);
    return ans;
}

int main()
{
    
    
    int n, q, l, r, val, opt;
    scanf("%d%d", &n, &q);
    for (int i = 1; i <= n; ++i) 
		scanf("%lld", a + i);
    build(1, 1, n);
	
    while (q--) {
    
    
        cin >> 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;
}

P3373 【模板】线段树 2

先乘再加,一开始把更新乘和更新加分开了,一直WA

合起来之后就AC了

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

ll a[N];
int M;

struct Node {
    
    
    ll l, r;
    ll sum, maxv, add_lazy, mul_lazy;
    // 更新 sum 和懒标 
    void updade(int add, int mul) {
    
    
    	sum = (sum * mul % M + add * (r - l + 1) % M) % M;
    	mul_lazy = mul_lazy * mul % M;
    	add_lazy = (add_lazy * mul % M + add) % M;
    }
    
} tree[N * 4];

void push_up(int p)
{
    
    
	tree[p].sum = (tree[p << 1].sum + tree[p << 1 | 1].sum) % M;
    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].add_lazy = 0;
    tree[p].mul_lazy = 1;
    if (l == r) {
    
    
        tree[p].sum = tree[p].maxv = a[l] % M;
        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 mul = tree[p].mul_lazy, add = tree[p].add_lazy;
    tree[p << 1].updade(add, mul);
    tree[p << 1 | 1].updade(add, mul);
    tree[p].add_lazy = 0;
    tree[p].mul_lazy = 1;
}

void update(int p, int l, int r, int add, int mul)
{
    
    
    if (l <= tree[p].l && tree[p].r <= r) {
    
    
        tree[p].updade(add, mul);
    } else {
    
    
        spread(p);
        int mid = tree[p].l + tree[p].r >> 1;
        if (l <= mid) 
			update(p << 1, l, r, add, mul);
        if (r > mid) 
			update(p << 1 | 1, l, r, add, mul);
        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)) % M;
    if (r > mid) 
		ans = (ans + query(p << 1 | 1, l, r)) % M;
    return ans;
}

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

P4588 [TJOI2018]数学计算

如果直接模拟的话,因为 mod 不一定是质数,而欧拉函数的复杂度是 O ( n l o g n ) O(\sqrt nlogn) O(n logn),计算 q 次会超时。

可以用线段树来维护乘积,a 数组来记录每次操作的乘数,先将 a 数组初始化为 1,再从 1 到 q 进行建树。如果进行 1 操作,就将 1 更新为新的值。如果进行 2 操作,就将之前的位置重置为 1 。

每次输出query(1, 1, i)(也就是tree[1].sum)

因为建树是从1到q,一开始写的1到n,一直RE

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

ll a[N];
int M;

struct Node {
    
    
    int l, r;
    ll sum, maxv, lazy;
    void updade(ll x) {
    
    
        sum = 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 = 1, 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) % M;
    //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);
        tree[p].sum = (tree[p << 1].sum * tree[p << 1 | 1].sum) % M;
        //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 = 1;
    int mid = tree[p].l + tree[p].r >> 1;
    if (l <= mid) 
		ans = (ans * query(p << 1, l, r)) % M;
    if (r > mid) 
		ans = (ans * query(p << 1 | 1, l, r)) % M;
    return ans;
}

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

P2574 XOR的艺术

因为对同一个区间异或两次,相当于没有异或,所以可以用懒标来记录异或的次数,最后对次数%2即可。

注意update的ans初始值

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

ll a[N];

struct Node {
    
    
    int l, r;
    ll sum, maxv, lazy;
    void updade() {
    
    
        sum = (r - l + 1) - sum;
        //maxv += x;
        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 % 2;
    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 n, q, l, r, val, opt;
    scanf("%d%d", &n, &q);
    for (int i = 1; i <= n; ++i) {
    
    
		scanf("%1lld", a + i);
	}
    build(1, 1, n);
    
    while (q--) {
    
    
        scanf("%d", &opt);
        if (opt == 1) {
    
    
            scanf("%d %d", &l, &r);
            printf("%lld\n", query(1, l, r));
        } else {
    
    
            scanf("%d %d", &l, &r);
            update(1, l, r);
        }
    }
    return 0;
}

P3374 【模板】树状数组 1

单点修改和区间查询

#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
typedef long long ll;

int n, m, a[N]; // a 为原数组 
ll c[N]; // c 为树状数组 

// 返回x的二进制数下最小2的次幂 
int lowbit(int x)
{
    
    
	return x & -x;
}
// 查询前缀和 
ll ask(int x)
{
    
    
	ll ans = 0;
	for (; x; x -= lowbit(x))
		ans += c[x];
	return ans;
}
// 单点增加 
void add(int x, int y)
{
    
    
	for (; x <= n; x += lowbit(x))
		c[x] += y;
}

int main(void)
{
    
    
	int op, x, y;
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; i++) {
    
    
		scanf("%d", &a[i]);
		add(i, a[i]); 
	}
	while (m--) {
    
    
		scanf("%d%d%d", &op, &x, &y);
		if (op == 1) {
    
    
			add(x, y);
		} else {
    
    
			ll res = ask(y) - ask(x - 1);
			printf("%lld\n", res);
		}
	}
	
	return 0;
}

P3368 【模板】树状数组 2

因为树状数组只能进行复杂度为 O(logn) 的单点修改,所以如果暴力进行区间修改的话,复杂度是 O(nlogn),会超时。

可以用树状数组 c 来记录每次区间修改的影响(即差分的思想),当对 [x, y] 进行加 k 时,就进行 add(x, k) 和 add(y+1, -k)。

又因为数组数组能够求前缀和,所以此时 ask(x) 即为修改操作对 a[x] 造成的影响。

所以修改后的值即为 a[x] 加上对 a[x] 造成的影响,即:a[x] + ask(x)

#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
typedef long long ll;

int n, m, a[N]; // a 为原数组 
ll c[N]; // c 为树状数组 

// 返回x的二进制数下最小2的次幂 
int lowbit(int x)
{
    
    
	return x & -x;
}
// 查询前缀和 
ll ask(int x)
{
    
    
	ll ans = 0;
	for (; x; x -= lowbit(x))
		ans += c[x];
	return ans;
}
// 单点增加 
void add(int x, int y)
{
    
    
	for (; x <= n; x += lowbit(x))
		c[x] += y;
}

int main(void)
{
    
    
	int op, x, y, k;
	scanf("%d%d", &n, &m);
	for (int i = 1; i <= n; i++) {
    
    
		scanf("%d", &a[i]);
	}
	while (m--) {
    
    
		scanf("%d", &op);
		if (op == 1) {
    
    
			scanf("%d%d%d", &x, &y, &k);
			add(x, k); add(y + 1, -k);
		} else {
    
    
			scanf("%d", &x);
			ll res = a[x] + ask(x);
			printf("%lld\n", res);
		}
	}
	
	return 0;
}

P1908 逆序对

树状数组 c 中记录每个数字出现的次数,因为数字比较大,要进行离散化

一开始离散化出了问题,用map进行离散化会超时,需要自己手动实现离散化

从右往左依次进行遍历,查询当前数大于多少个它右边的数字,并加到res中

#include <bits/stdc++.h>
using namespace std;
const int N = 1e6 + 5;
typedef long long ll;

int n, m, a[N], b[N]; // a 为原数组 
ll c[N]; // c 为树状数组 

// 返回x的二进制数下最小2的次幂 
int lowbit(int x)
{
    
    
	return x & -x;
}
// 查询前缀和 
ll ask(int x)
{
    
    
	ll ans = 0;
	for (; x; x -= lowbit(x))
		ans += c[x];
	return ans;
}
// 单点增加 
void add(int x, int y)
{
    
    
	for (; x <= n; x += lowbit(x))
		c[x] += y;
}

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(void)
{
    
    
	int op, x, y;
	scanf("%d", &n);
	for (int i = 1; i <= n; i++) {
    
    
		scanf("%d", &a[i]);
		b[i] = a[i];
	}
	discrete();
	int idx;
	ll res = 0;
	for (int i = n; i >= 1; i--) {
    
    
		// 离散化后的数字 
		idx = lower_bound(b + 1, b + m + 1, a[i]) - b;
		res += ask(idx - 1);
		add(idx, 1);
	}
	
	printf("%lld\n", res);
	
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_43772166/article/details/108879085