以下是同时具有区间修改和区间赋值功能的线段树的实现:
```cpp
#include <bits/stdc++.h>
using namespace std;
const int MAXN = 100005;
int a[MAXN], sum[MAXN<<2], add[MAXN<<2], setv[MAXN<<2];
void pushup(int rt) {
sum[rt] = sum[rt<<1] + sum[rt<<1|1];
}
void pushdown(int rt, int ln, int rn) {
if (setv[rt] != -1) {
setv[rt<<1] = setv[rt<<1|1] = setv[rt];
add[rt<<1] = add[rt<<1|1] = 0;
sum[rt<<1] = setv[rt] * ln;
sum[rt<<1|1] = setv[rt] * rn;
setv[rt] = -1;
}
if (add[rt]) {
add[rt<<1] += add[rt];
add[rt<<1|1] += add[rt];
sum[rt<<1] += add[rt] * ln;
sum[rt<<1|1] += add[rt] * rn;
add[rt] = 0;
}
}
void build(int l, int r, int rt) {
setv[rt] = -1;
add[rt] = 0;
if (l == r) {
sum[rt] = a[l];
return;
}
int mid = (l + r) >> 1;
build(l, mid, rt<<1);
build(mid+1, r, rt<<1|1);
pushup(rt);
}
void update_add(int L, int R, int C, int l, int r, int rt) {
if (L <= l && r <= R) {
add[rt] += C;
sum[rt] += C * (r-l+1);
return;
}
int mid = (l + r) >> 1;
pushdown(rt, mid-l+1, r-mid);
if (L <= mid) update_add(L, R, C, l, mid, rt<<1);
if (R > mid) update_add(L, R, C, mid+1, r, rt<<1|1);
pushup(rt);
}
void update_set(int L, int R, int C, int l, int r, int rt) {
if (L <= l && r <= R) {
setv[rt] = C;
add[rt] = 0;
sum[rt] = C * (r-l+1);
return;
}
int mid = (l + r) >> 1;
pushdown(rt, mid-l+1, r-mid);
if (L <= mid) update_set(L, R, C, l, mid, rt<<1);
if (R > mid) update_set(L, R, C, mid+1, r, rt<<1|1);
pushup(rt);
}
int query(int L, int R, int l, int r, int rt) {
if (L <= l && r <= R) {
return sum[rt];
}
int mid = (l + r) >> 1;
int ans = 0;
pushdown(rt, mid-l+1, r-mid);
if (L <= mid) ans += query(L, R, l, mid, rt<<1);
if (R > mid) ans += query(L, R, mid+1, r, rt<<1|1);
return ans;
}
int main() {
int n, m;
cin >> n >> m;
for (int i = 1; i <= n; i++) {
cin >> a[i];
}
build(1, n, 1);
while (m--) {
int op, l, r, c;
cin >> op >> l >> r >> c;
if (op == 1) {
update_add(l, r, c, 1, n, 1);
} else if (op == 2) {
update_set(l, r, c, 1, n, 1);
} else {
cout << query(l, r, 1, n, 1) << endl;
}
}
return 0;
}
```
该线段树的主要思路是在普通线段树的基础上增加一个 `setv` 数组,表示当前区间是否被赋值。若 `setv[rt]` 不为 -1,则表示当前区间被赋值为 `setv[rt]`,同时 `add[rt]` 被清空。若 `add[rt]` 不为 0,则表示当前区间被加上了 `add[rt]`,同时 `setv[rt]` 被清空。这样就能同时支持区间修改和区间赋值了。
需要注意的一点是,在进行区间赋值操作时,应当将左右儿子的 `add` 数组清空,因为此时区间被完全覆盖,之前的修改操作会被覆盖掉,没有意义了。