赞
踩
luogu-P3372-线段树1
luogu-P3373-线段树2
两道题都是比较基础的模板题,线段树1只有区间加、区间和查询,线段树2增加了区间乘的操作。
这篇文章不是讲题解的(洛谷大神们写的老清楚了,OI Wiki写的也很好),而是讲一些刚接触线段树(像我一样)在写的时候容易遇到的疑惑和细节问题。
if (l <= mid) update_add(curr<<1, l, r, k); // 根据左右端点和中点的位置关系进行分类
if (r > mid) update_add(curr<<1|1, l, r, k);
其实难点就一个“先乘后加”,需要注意每一次的乘法因子对于其作用的区间和、加法标记和乘法标记都是乘的关系;由于先乘后加,加法懒惰标记已经涵盖了之前的所有乘法的内容,因此可以在pushdown的时候直接加入。pushdown唯一需要关注的就是先对下一层执行乘法,再执行加法,分开写可能会比较清晰(见代码)。
#include <iostream> using namespace std; const int n_MAX = 1e5+5; // 没有+5导致数据给到100000的时候数组a溢出然后n被覆盖! typedef long long ll; struct node { ll l, r; ll mul; ll add; ll sum; }tree[4*n_MAX]; ll a[n_MAX]; ll n, m, p; void pushup(ll curr) { tree[curr].sum = (tree[curr<<1].sum + tree[(curr<<1)+1].sum) % p; } void build(ll curr, ll l, ll r) { tree[curr].l = l; tree[curr].r = r; tree[curr].sum = 0; tree[curr].mul = 1; // 乘法的单位元是1 if (l == r) { tree[curr].sum = a[l] % p; return; } ll mid = l + ((r - l) >> 1); build(curr<<1, l, mid); build(curr<<1|1, mid+1, r); pushup(curr); return; } void pushdown(ll curr) { // 不要画蛇添足,不需要判断是否是叶子结点,不必增加add和mul是否有的条件 ll leftlen = tree[curr<<1].r - tree[curr<<1].l + 1; ll rightlen = tree[(curr<<1)+1].r - tree[(curr<<1)+1].l + 1; tree[curr<<1].sum = (tree[curr<<1].sum * tree[curr].mul) % p; // 先乘后加 tree[(curr<<1)+1].sum = (tree[(curr<<1)+1].sum * tree[curr].mul) % p; tree[curr<<1].mul = (tree[curr<<1].mul * tree[curr].mul) % p; tree[(curr<<1)+1].mul = (tree[(curr<<1)+1].mul * tree[curr].mul) % p; tree[curr<<1].add = (tree[curr<<1].add * tree[curr].mul) % p; tree[(curr<<1)+1].add = (tree[(curr<<1)+1].add * tree[curr].mul) % p; tree[curr<<1].sum = (tree[curr<<1].sum + (leftlen * tree[curr].add) % p) % p; tree[curr<<1].add = (tree[curr<<1].add + tree[curr].add) % p; tree[(curr<<1)+1].sum = (tree[(curr<<1)+1].sum + (rightlen * tree[curr].add) % p) % p; tree[(curr<<1)+1].add = (tree[(curr<<1)+1].add + tree[curr].add) % p; tree[curr].add = 0; tree[curr].mul = 1; } void update_add(ll curr, ll l, ll r, ll k) { if (l <= tree[curr].l && r >= tree[curr].r) { tree[curr].sum = (tree[curr].sum + (tree[curr].r - tree[curr].l + 1) * k) % p; tree[curr].add = (tree[curr].add + k) % p; return; } ll mid = tree[curr].l + ((tree[curr].r - tree[curr].l) >> 1); pushdown(curr); if (l <= mid) update_add(curr<<1, l, r, k); // 根据左右端点和中点的位置关系进行分类 if (r > mid) update_add(curr<<1|1, l, r, k); pushup(curr); } void update_mul(ll curr, ll l, ll r, ll k) { if (l <= tree[curr].l && r >= tree[curr].r) { tree[curr].sum = (tree[curr].sum * k) % p; tree[curr].add = (tree[curr].add * k) % p; tree[curr].mul = (tree[curr].mul * k) % p; // debug: 乘法系数不是相加,是连乘 return; } ll mid = tree[curr].l + ((tree[curr].r - tree[curr].l) >> 1); pushdown(curr); if (l <= mid) update_mul(curr<<1, l, r, k); // 根据左右端点和中点的位置关系进行分类 if (r > mid) update_mul(curr<<1|1, l, r, k); pushup(curr); } ll query(ll curr, ll l, ll r) { if (l <= tree[curr].l && r >= tree[curr].r) return tree[curr].sum; ll mid = tree[curr].l + ((tree[curr].r - tree[curr].l) >> 1); pushdown(curr); ll sum = 0; if (l <= mid) sum = (sum + query(curr<<1, l, r)) % p; if (r > mid) sum = (sum + query(curr<<1|1, l, r)) % p; return sum; } int main() { scanf("%lld%lld%lld", &n, &m, &p); for (ll i = 1; i <= n; i++) { scanf("%lld", &a[i]); } build(1, 1, n); while (m--) { ll type, x, y, k; scanf("%lld", &type); switch (type) { case 1: scanf("%lld%lld%lld", &x, &y, &k); update_mul(1, x, y, k); break; case 2: scanf("%lld%lld%lld", &x, &y, &k); update_add(1, x, y, k); break; case 3: scanf("%lld%lld", &x, &y); ll res = query(1, x, y); printf("%lld\n", res); break; } } return 0; }
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。