线段树->线段树2(洛谷3373)
题意:n个数m个查询,三种操作。 1:区间的数都+k。2:区间的数都*k。3:求区间和
分析:考虑如何设计区间更新规则。 对于lazy的更新:如果乘进来,那么和跟乘积都乘,然后和加上和。如果和进来直接加。如果同时进来,也是先乘再加。 对于区间sum的更新,按照先乘再加即可。 (突然发现lazy标记的更新好像跟区间update的更新一样,不过update是更新的查询结果,而lazy是更新合并区间更改的值)
long long mod;
struct Node{
long long sum;
long long add;
long long mul;
bool is_lazy = false;
Node(bool state = false): sum(0ll), add(0ll), mul(1ll), is_lazy(state){}
//合并区间
Node operator +(const Node& other){
Node rst = *this;
rst.sum = (rst.sum + other.sum) % mod;
return rst;
}
//合并懒标记
void operator |=(const Node& other){
mul = mul * other.mul % mod;
add = (add * other.mul + other.add) % mod;
is_lazy = true;
}
//这个函数定义如何在区间上设计规则更新懒标记的各种值
void update(const Node& other, int length = 1){
long long cur = sum;
sum = sum * other.mul % mod;
sum = (sum + length * other.add % mod) % mod;
}
bool isLazy()const{
return is_lazy;
}
bool setLazy(){
is_lazy = true;
}
};
template<class T>
class SegmentTree{
private:
vector<T> st_, lazy_;
int size_;
const T flag_ = {};
T conquer(T a, T b){
return a + b;
}
void propagate(int p, int l, int r){
if (lazy_[p].isLazy()){
st_[p].update(lazy_[p], r - l + 1); //update更新该区间跟flag值
if (l != r){
lazy_[p << 1] |= lazy_[p];
lazy_[p << 1 | 1] |= lazy_[p]; // |= 叠加flag懒人标记重载运算符
}
lazy_[p] = flag_;
}
}
//以下部分暂时不需要改
void build(const vector<T>& s, int p, int l, int r){
if (l == r){
st_[p] = s[l];
}
else{
int mid = (l + r) >> 1;
build(s, p << 1, l, mid);
build(s, p << 1 | 1, mid + 1, r);
st_[p] = conquer(st_[p << 1], st_[p << 1 | 1]);
}
}
void update(int p, int l, int r, int i, int j, T val){
propagate(p, l, r); //必须放在if语句前,因为如果是子区间要被上一层递归调用合并
if (i > j) {
return;
}
if (l >= i && r <= j){
lazy_[p] = val;
propagate(p, l, r); //更新st[p],并将lazy值往下传递一层
}
else{
int mid = (l + r) >> 1;
update(p << 1, l, mid, i, std::min(mid, j), val);
update(p << 1 | 1, mid + 1, r, std::max(i, mid + 1), j, val);
st_[p] = conquer(st_[p << 1], st_[p << 1 | 1]);
}
}
//如果把语句改成这样,好像就不需要flag来判断非法了。所有返回值均合法。
T query(int p, int l, int r, int i, int j){
propagate(p, l, r);
if (l >= i && r <= j){
return st_[p];
}
int mid = (l + r) >> 1;
if (j <= mid){
return query(p << 1, l, mid, i, j);
}
if (i > mid){
return query(p << 1 | 1, mid + 1, r, i, j);
}
return conquer(query(p << 1, l, mid, i, j), query(p << 1 | 1, mid + 1, r, i, j));
}
public:
explicit SegmentTree(int sz): size_(sz){ //这里有多少传多少,不要传n + 1,n代表具体的数字数量,不代表下标。 BIT, dsu 代表的是下标。
st_.resize(4 * size_);
lazy_.assign(4 * size_, flag_);
}
explicit SegmentTree(vector<T>& a):SegmentTree(int(a.size()) - 1){ //约定下标从1开始,那么这里一定a.size() -1,不然会导致计算结果错误。
build(a, 1, 1, size_);
}
void update(int i, int j, T val){
update(1, 1, size_, i, j, val);
}
T query(int i, int j){
return query(1, 1, size_, i, j);
}
};
void solve(){
int n, m;
cin >> n >> m >> mod;
vector<Node> a(n + 1);
for (int i = 1; i <= n; ++i){
cin >> a[i].sum;
}
SegmentTree<Node> st(a);
while (m --){
int t, l, r;
cin >> t >> l >> r;
if (t == 1){
long long x;
cin >> x;
Node temp{true};
temp.mul = x;
st.update(l, r, temp);
}
else if (t == 2){
long long x;
cin >> x;
Node temp{true};
temp.add = x;
st.update(l, r, temp);
}
else{
cout << st.query(l, r).sum << '\n';
}
}
}

浙公网安备 33010602011771号