00x1.线段树

线段树维护区间和

LuoguP3372 线段树(分治+懒标记)模板
线段树基础操作
1.建立线段树 build(int p,int l,int r)
2.区间修改 update(int p,int x,int y,LL k)
3.区间查询 query(int p,int x,int y)

#include<iostream>
using namespace std;
#define lc p << 1
#define rc p << 1|1
const int N = 500005;
typedef long long LL;
LL w[N];  
struct node {
    int l, r;  // 区间端点
    LL sum;    // 区间和
    LL add;    // lazytag
}tr[4 * N];


void pushup(int p) //回溯时向上更新
{
    tr[p].sum = tr[lc].sum + tr[rc].sum;
}

void pushdown(int p) //向下传递lazytag
{
    if (tr[p].add)
    {
        tr[lc].sum += tr[p].add * (tr[lc].r - tr[lc].l + 1); //兑现左子树的lazytag
        tr[rc].sum += tr[p].add * (tr[rc].r - tr[rc].l + 1); //兑现右子树的lazytag
        tr[lc].add += tr[p].add;  //lazytag下传左子树
        tr[rc].add += tr[p].add;  //lazytag下传右子树
        tr[p].add = 0;     //当前节点lazytag清零
    }
}


//递归建树
void build(int p, int l, int r)
{
    tr[p] = { l, r, w[l] ,0 };   //无需在意非叶子节点的w值,回溯时会更新
    if (l == r) return;       //叶子节点返回
    int m = (l + r) >> 1;     //非叶子节点递归(先序遍历)
    build(lc, l, m);
    build(rc, m + 1, r);
    pushup(p);  //回溯时更新非叶子节点信息
}


//区间查询(求区间和[x,y])
//1.[x,y]完全覆盖当前节点区间:立即回溯
//2.左子节点与[x,y]重叠:递归左子树
//3.右子节点与[x,y]重叠:递归右子树
LL query(int p, int x, int y)
{
    if (x > tr[p].r || y < tr[p].l) return 0;
    if (x <= tr[p].l && tr[p].r <= y)
        return tr[p].sum;
    int m = tr[p].l + tr[p].r >> 1;
    LL sum = 0;
    pushdown(p);
    if (x <= m) sum += query(lc, x, y);
    if (y > m) sum += query(rc, x, y);
    return sum;
}

//区间修改(区间[x,y]位置上的数字都加上k)
//1.[x,y]完全覆盖当前节点区间:修改该节点的sum,打上懒标记,立即回溯
//2.左子节点与[x,y]重叠:递归左子树,同时还清当前节点的懒标记,把懒标记传递给左儿子
//3.右子节点与[x,y]重叠:递归右子树,同时还清当前节点的懒标记,把懒标记传递给右儿子
void update(int p, int x, int y, LL k)
{
    if (x > tr[p].r || y < tr[p].l) return;
    if (x <= tr[p].l && tr[p].r <= y)
    {
        tr[p].sum += (tr[p].r - tr[p].l + 1) * k;
        tr[p].add += k;
        return;
    }

    int m = tr[p].l + tr[p].r >> 1;
    pushdown(p);
    if (x <= m) update(lc, x, y, k);
    if (y > m) update(rc, x, y, k);
    pushup(p);
}


int main()
{
    LL n, m, op, x, y, k;
    cin >> n >> m;
    for (int i = 1; i <= n; i++) cin >> w[i];

    build(1, 1, n);
    while (m--) {
        cin >> op >> x >> y;
        if (op == 2)cout << query(1, x, y) << endl;
        else cin >> k, update(1, x, y, k);
    }

    return 0;
}

(最后一个点卡了很久,居然是因为k忘记long long)

线段树维护区间最小值

LuoguP1816 忠诚

#include<iostream>
#include<climits>
#include<vector>
using namespace std;
const int N = 100005;
#define lc p << 1
#define rc p << 1|1
typedef long long LL;
int n, m, a, b, w[N];
struct node
{
	int l, r, minn = INT_MAX;
}tr[4 * N];

vector<int>ans;

void pushup(int p)
{
	tr[p].minn = min(tr[lc].minn, tr[rc].minn);
}

void build(int p, int l, int r)
{
	tr[p] = { l,r };
	if (l == r)
	{
		tr[p].minn = w[l];
		return;
	}
	int mid = (l + r) >> 1;
	build(lc, l, mid);
	build(rc, mid + 1, r);
	pushup(p);
}


int query(int p, int l, int r)
{
	if (l <= tr[p].l && tr[p].r <= r) return tr[p].minn;

	int mid = (tr[p].l + tr[p].r) >> 1;
	int res = INT_MAX;
	if (l <= mid) res = min(res, query(lc, l, r));
	if (r > mid) res = min(res, query(rc, l, r));
	return res;
}

int main()
{
	cin >> m >> n;
	for (int i = 1; i <= m; i++) cin >> w[i];
	build(1, 1, m);
	while (n--)
	{
		cin >> a >> b;
		ans.push_back(query(1, a, b));
	}
	for (auto x : ans) cout << x << " ";

	return 0;
}

(水的很,甚至不用区间修改)

线段树维护差分信息

Luogu P1438 无聊的数列
本题的题意就是对差分数组进行区间修改,对应着原式加上等差数列
然后访问第\(p\)个数就是访问差分区间的\(\displaystyle{\sum_{i=1}^pa[i]}\)

#include<iostream>
using namespace std;
#define lc p << 1
#define rc p << 1|1
typedef long long LL;
const int N = 1e5 + 5;
int n, m, K, D, q, op, l, r;
LL a[N];
struct Node {
	LL l, r, sum, add;
}tr[4 * N];

void pushup(int p)
{
	tr[p].sum = tr[lc].sum + tr[rc].sum;
}

void pushdown(int p)
{
	if (tr[p].add)
	{
		tr[lc].sum += tr[p].add * (tr[lc].r - tr[lc].l + 1);
		tr[rc].sum += tr[p].add * (tr[rc].r - tr[rc].l + 1);
		tr[lc].add += tr[p].add;
		tr[rc].add += tr[p].add;
		tr[p].add = 0;
	}
}
void build(int p, int l, int r)
{
	tr[p] = { l, r, 0, 0 };
	if (l == r) {
		tr[p].sum = a[l];
		return;
	}
	int mid = (l + r) >> 1;
	build(lc, l, mid);
	build(rc, mid + 1, r);
	pushup(p);
}

LL query(int p,int x,int y)
{
	if (x <= tr[p].l && tr[p].r <= y) return tr[p].sum;
	LL res = 0;
	int mid = tr[p].l + tr[p].r >> 1;
	pushdown(p);
	if (x <= mid) res += query(lc, x, y);
	if (mid < y) res += query(rc, x, y);
	return res;
}


void update(int p, int x, int y, LL k)
{
	if (x <= tr[p].l && tr[p].r <= y)
	{
		tr[p].sum += k * (tr[p].r - tr[p].l + 1);
		tr[p].add += k;
		return;
	}
	int mid = (tr[p].l + tr[p].r) >> 1;
	pushdown(p);
	if (x <= mid) update(lc, x, y, k);
	if (y > mid) update(rc, x, y, k);
	pushup(p);
}


int main()
{
	cin >> n >> m;
	for (int i = 1; i <= n; i++) cin >> a[i];
	for (int i = n; i > 1; i--) a[i] -= a[i - 1];
	build(1, 1, n);

	while (m--)
	{
		cin >> op;
		if (op == 1)
		{
			cin >> l >> r >> K >> D;
			update(1, l, l, K);                        //a[l] + k
			if (l + 1 <= r) update(1, l + 1, r, D);    //a[l+1~r] + D
			if (r < n) update(1, r + 1, r + 1, -(K + D * (r - l))); //a[r+1] - (K + D*(r-l))
		}
		else
		{
			cin >> q;
			cout << query(1, 1, q) << endl;
		}
	}

	return 0;
}

(有两个逆天小错误,1和l要分清,query的返回值要LL)

线段树维护区间异或信息

Luogu P3870 开关
异或信息的维护与其他运算有些许不同(异或运算满足结合律,是能用线段树维护的前提)
区间\([a,b]\)中为1的数的个数为\(x\),为0的数的个数为\(y\),那么就有\(x+y=b-a+1\),每一次取反就是\(x,y\)互换,由此可以实现懒标记兑现和修改的操作
其他就是线段树的模板了

#include<iostream>
using namespace std;
const int N = 100005;
int a, b, c, n, m;
#define lc p << 1
#define rc p << 1|1
struct node
{
    int l, r, sum, add;
}tr[4 * N];


void pushup(int p)
{
    tr[p].sum = tr[lc].sum + tr[rc].sum;
}


void pushdown(int p)
{
    if (tr[p].add)
    {
        tr[lc].sum = (tr[lc].r - tr[lc].l + 1) - tr[lc].sum;
        tr[rc].sum = (tr[rc].r - tr[rc].l + 1) - tr[rc].sum;
        tr[lc].add ^= 1;
        tr[rc].add ^= 1;
        tr[p].add = 0;
    }
}

void build(int p, int l, int r)
{
    tr[p] = { l,r };
    if (l == r)
    {
        tr[p].sum = 0;
        return;
    }

    int mid = (l + r) >> 1;
    build(lc, l, mid);
    build(rc, mid + 1, r);
    pushup(p);
}

int query(int p, int x, int y)
{
    if (x <= tr[p].l && tr[p].r <= y) return tr[p].sum;
    int res = 0;
    int mid = (tr[p].l + tr[p].r) >> 1;
    pushdown(p);
    if (x <= mid) res += query(lc, x, y);
    if (mid < y) res += query(rc, x, y);
    return res;
}

void update(int p, int x, int y)
{
    if (x <= tr[p].l && tr[p].r <= y)
    {
        tr[p].sum = (tr[p].r - tr[p].l + 1) - tr[p].sum;
        tr[p].add ^= 1;
        return;
    }
    int mid = (tr[p].l + tr[p].r) >> 1;
    pushdown(p);
    if (x <= mid) update(lc, x, y);
    if (mid < y) update(rc, x, y);
    pushup(p);
}

int main()
{
    cin >> n >> m;
    build(1, 1, n);

    while (m--)
    {
        cin >> c >> a >> b;
        if (c == 0) update(1, a, b);
        else cout << query(1, a, b) << endl;
    }

    return 0;
}
posted @ 2025-06-06 15:44  _P_D_X  阅读(11)  评论(0)    收藏  举报