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;
}

浙公网安备 33010602011771号