题解 P3373 【【模板】线段树 2】
看到dalao们写的基本上都是数组版的线段树,我就写一个链表版的线段树吧(真的比数组简单,我就是看到链表版的线段树才真正会线段树的)。
这道题比一般的线段树也有所不同,多了一个乘法而已。对付的办法也很容易想到,就是多开一个mul的标记,pushdown的时候就是先下传mul再下传add就可以了(乘法比加法优先级要高)。
具体的乘法cover就是把当前节点的sum,add和mul都乘上一个数。 加法cover和原来一样(add加上一个数,sum加上add乘区间长度)。
来一发惊世骇俗的好玩的链表线段树。
#include<cstdio>
#include<cstring>
#include<iostream>
#include<algorithm>
#include<vector>
int n, m, p, a[100005];
struct Segtree {
int l, r;
Segtree *lc, *rc;
long long sum, add, mul;
Segtree(int l, int r, Segtree *lc, Segtree *rc) //构造函数1,里面封装了build时候要用的pushup()
: l(l), r(r), lc(lc), rc(rc), sum(lc->sum + rc->sum), add(0), mul(1) {}
Segtree(int l, int r, Segtree *lc, Segtree *rc, int sum) //构造函数2,对子节点使用
: l(l), r(r), lc(lc), rc(rc), sum(sum), add(0), mul(1) {}
void covermul(long long rhs) { //乘法cover
sum = sum * rhs % p;
mul = mul * rhs % p;
add = add * rhs % p;
}
void coveradd(long long rhs) { //加法cover
add = (add + rhs) % p;
sum = (sum + rhs * (r - l + 1)) % p;
}
void pushup() {
sum = (lc->sum + rc->sum) % p;
}
void pushdown() { //先cover乘法再cover加法
lc->covermul(mul); lc->coveradd(add);
rc->covermul(mul); rc->coveradd(add);
mul = 1; add = 0;
}
void multiply(int l, int r, long long rhs) { //乘法updata
if (l > this->r || r < this->l) return;
else if (l <= this->l && this->r <= r) covermul(rhs);
else {
pushdown();
lc->multiply(l, r, rhs);
rc->multiply(l, r, rhs);
pushup();
}
return;
}
void addition(int l, int r, long long rhs) { //加法updata
if (l > this->r || r < this->l) return;
else if (l <= this->l && this->r <= r) coveradd(rhs);
else {
pushdown();
lc->addition(l, r, rhs);
rc->addition(l, r, rhs);
pushup();
}
return;
}
long long query(int l, int r) {
if (l > this->r || r < this->l) return 0;
if (l <= this->l && this->r <= r) return sum;
pushdown();
return (lc->query(l, r) + rc->query(l, r)) % p;
}
static Segtree *build(int l, int r) { //有趣的build
if (l > r) return NULL;
else if (l == r) return new Segtree(l, r, NULL, NULL, a[l]);
else {
int mid = (l + r) >> 1;
return new Segtree(l, r, build(l, mid), build(mid + 1, r));
}
}
}*root;
int main() {
scanf("%d%d%d", &n, &m, &p);
for (int i = 1; i <= n; i++)
scanf("%d", &a[i]);
root = Segtree::build(1, n); //链表的专属build
for (int i = 1; i <= m; i++) {
int opt;
scanf("%d", &opt);
if (opt == 1) {
int l, r, k;
scanf("%d%d%d", &l, &r, &k);
root->multiply(l, r, k);
}
else if (opt == 2) {
int l, r, k;
scanf("%d%d%d", &l, &r, &k);
root->addition(l, r, k);
}
else if (opt == 3) {
int l, r;
scanf("%d%d", &l, &r);
printf("%lld\n", root->query(l, r));
}
}
return 0;
}