HDU - 4578 Transformation

题意:

给定一个长度为 n n n 的数组 a a a,有 m m m 次操作,① 1 , l , r , x 1, l, r, x 1,l,r,x,区间加 x x x;② 2 , l , r , x 2, l, r, x 2,l,r,x,区间乘 x x x;③ 3 , l , r , x 3, l, r, x 3,l,r,x,区间赋值为 x x x;④ 4 , l , r , p 4, l, r, p 4,l,r,p,询问 ∑ i = l r a i p \sum\limits_{i = l}^{r}a_i^p i=lraip,答案模 10007 10007 10007 ( n , m ≤ 1 0 5 , 1 ≤ p ≤ 3 ) (n, m \leq 10^5, 1 \leq p \leq 3) (n,m105,1p3)

链接:

https://vjudge.net/problem/HDU-4578

解题思路:

首先将区间赋值 x x x 转化为区间乘 0 0 0 再加 x x x,只需考虑乘法和加法操作。用线段树维护区间 p p p 次方和,同时维护乘法与加法标记。标记之间可能相互影响,应依次更新,即给定优先级,那么这里假定标记下推是先乘后加。

标记下推的实质是对子区间的区间更新,即下推标记 ( Δ m , Δ a ) (\Delta m, \Delta a) (Δm,Δa),即子区间先乘 Δ m \Delta m Δm 后加 Δ a \Delta a Δa 时的更新。记子区间标记为 ( m , a ) (m, a) (m,a),则子区间内 a i a_i ai 的实际值记为 x i x_i xi,子区间的子区间当前值记为 y i y_i yi,注意 y i y_i yi 并非实际值,当父结点上的标记完全下推时才被更新为实际值 。首先考虑标记的下放(更新子区间意味着子区间的子区间也应被更新,叠加上懒惰标记),
先乘 Δ m : ( m y i + a ) ⇒ ( m y i + a ) ∗ Δ m = ( m Δ m y i + a Δ m ) = ( m ′ y i + a ′ ) \Delta m: (my_i + a) \Rightarrow (my_i + a) * \Delta m = (m\Delta m y_i + a \Delta m) = (m'y_i + a') Δm:(myi+a)(myi+a)Δm=(mΔmyi+aΔm)=(myi+a)
再加 Δ a : ( m ′ y i + a ′ ) ⇒ ( m ′ y i + a ′ ) + Δ a = ( m ′ y i + a ′ + Δ a ) = ( m ′ y i + a ′ ′ ) \Delta a: (m'y_i + a') \Rightarrow (m'y_i + a') + \Delta a = (m'y_i + a' + \Delta a) = (m'y_i + a'') Δa:(myi+a)(myi+a)+Δa=(myi+a+Δa)=(myi+a)

以上是对标记的维护,那么接下来考虑标记下推时对 x i x_i xi 的直接更新,
先乘 Δ m : \Delta m: Δm:
{ ∑ x i ⇒ ∑ ( x i ∗ Δ m ) = Δ m ∑ x i = ∑ x i ′ ∑ x i 2 ⇒ ∑ ( x i ∗ Δ m 2 ) = Δ m 2 ∑ x i 2 = ∑ x i ′ 2 ∑ x i 3 ⇒ ∑ ( x i ∗ Δ m 3 ) = Δ m 3 ∑ x i 3 = ∑ x i ′ 3 \begin{cases} \sum x_i \Rightarrow \sum (x_i * \Delta m) = \Delta m \sum x_i = \sum x_i'\\ \sum x_i^2 \Rightarrow \sum (x_i * \Delta m^2) = \Delta m^2 \sum x_i^2 = \sum x_i'^2\\ \sum x_i^3 \Rightarrow \sum (x_i * \Delta m^3) = \Delta m^3 \sum x_i^3 = \sum x_i'^3\\ \end{cases} xi(xiΔm)=Δmxi=xixi2(xiΔm2)=Δm2xi2=xi2xi3(xiΔm3)=Δm3xi3=xi3
再加 Δ a : \Delta a: Δa:
{ ∑ x i ′ ⇒ ∑ ( x i ′ + Δ a ) = l e n ∗ Δ a + ∑ x i ′ ∑ x i ′ 2 ⇒ ∑ ( x i ′ + Δ a ) 2 = l e n ∗ Δ a 2 + 2 ∗ Δ a ∗ ∑ x i ′ + ∑ x i ′ 2 ∑ x i ′ 3 ⇒ ∑ ( x i ′ + Δ a ) 3 = … \begin{cases} \sum x_i' \Rightarrow \sum (x_i' + \Delta a) = len * \Delta a + \sum x_i' \\ \sum x_i'^2 \Rightarrow \sum (x_i' + \Delta a)^2 = len * \Delta a^2 + 2*\Delta a*\sum x_i' + \sum x_i'^2 \\ \sum x_i'^3 \Rightarrow \sum (x_i' + \Delta a)^3 = \dots \end{cases} xi(xi+Δa)=lenΔa+xixi2(xi+Δa)2=lenΔa2+2Δaxi+xi2xi3(xi+Δa)3=
至此,按标记优先级顺序下放,完成了分级的标记维护和区间更新,固然可以一步到位,但分开维护逻辑较为清晰,若不将赋值操作转化,那么赋值标记优先级可以设置为最高。

标记下推实质是对子区间的更新,那么 u p d a t e update update p u s h D o w n pushDown pushDown 中的更新部分,可以写成 m o d i f y modify modify 函数调用, u p d a t e update update 中将 0 0 0 号结点拿出来放上标记,下推给当前结点。

参考代码:
#include<bits/stdc++.h>
 
using namespace std;
typedef long long ll;
typedef pair<int, int> pii;
#define sz(a) ((int)a.size())
#define pb push_back
#define lson (rt << 1)
#define rson (rt << 1 | 1)
#define gmid (l + r >> 1)
const int maxn = 1e5 + 5;
const int inf = 0x3f3f3f3f;
const int mod = 1e4 + 7;

int n, m;

struct SegTree{

    int sum1[maxn << 2], sum2[maxn << 2], sum3[maxn << 2], mul[maxn << 2], add[maxn << 2];
    void pushUp(int rt){

        sum1[rt] = (sum1[lson] + sum1[rson]) % mod;
        sum2[rt] = (sum2[lson] + sum2[rson]) % mod;
        sum3[rt] = (sum3[lson] + sum3[rson]) % mod;
    }
    void build(int l, int r, int rt){

        mul[rt] = 1, add[rt] = 0;
        sum1[rt] = sum2[rt] = sum3[rt] = 0;
        if(l == r) return;
        int mid = gmid;
        build(l, mid, lson);
        build(mid + 1, r, rson);
    }
    void modify(int rt, int son, int len){

        int M = mul[rt], A = add[rt];
        
        (mul[son] *= M) %= mod;
        (add[son] *= M) %= mod;

        (add[son] += A) %= mod;

        (sum3[son] *= M * M % mod * M % mod) %= mod;
        (sum2[son] *= M * M % mod) %= mod;
        (sum1[son] *= M) %= mod;

        (sum3[son] += 3 * sum2[son] % mod * A % mod + 3 * sum1[son] % mod * A % mod * A % mod + len * A % mod * A % mod * A % mod) %= mod;
        (sum2[son] += 2 * sum1[son] % mod * A % mod + len * A % mod * A % mod) %= mod;
        (sum1[son] += len * A % mod) %= mod;
    }
    void pushDown(int l, int r, int rt){

        int mid = gmid;
        modify(rt, lson, mid - l + 1);
        modify(rt, rson, r - mid);
        mul[rt] = 1, add[rt] = 0;
    }
    void update(int l, int r, int rt, int L, int R, int M, int A){

        if(l >= L && r <= R){

            mul[0] = M, add[0] = A;
            modify(0, rt, r - l + 1);
            return;
        }
        int mid = gmid;
        pushDown(l, r, rt);
        if(L <= mid) update(l, mid, lson, L, R, M, A);
        if(R > mid) update(mid + 1, r, rson, L, R, M, A);
        pushUp(rt);
    }
    int query(int l, int r, int rt, int L, int R, int p){

        if(l >= L && r <= R){

            return p == 1 ? sum1[rt] : p == 2 ? sum2[rt] : sum3[rt];
        }
        int mid = gmid, ret = 0;
        pushDown(l, r, rt);
        if(L <= mid) ret += query(l, mid, lson, L, R, p);
        if(R > mid) ret += query(mid + 1, r, rson, L, R, p);
        return ret % mod;
    }
} tr;

int main(){

    ios::sync_with_stdio(0); cin.tie(0);
    while(cin >> n >> m && n){

        tr.build(1, n, 1);
        while(m--){

            int opt, x, y, z; cin >> opt >> x >> y >> z;
            if(opt == 1){

                tr.update(1, n, 1, x, y, 1, z);
            }
            else if(opt == 2){

                tr.update(1, n, 1, x, y, z, 0);
            }
            else if(opt == 3){

                tr.update(1, n, 1, x, y, 0, z);
            }
            else{

                int ret = tr.query(1, n, 1, x, y, z);
                cout << ret << "\n";
            }
        }
    }
    return 0;;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值