线段树多个信息的传递(加法和乘法) seq

    用线段树对一段的数据进行单纯的加法、乘法,这些都不难,只需要在某个区间内做个懒标记,然后经过每个结点时都把懒标记往下传递。
    如果有多个标记,最简单的就是加减混合。多个信息的难点就在于如何把信息结合起来,要知道5 + (6 * 2)是不等于(5 + 6) * 2的。
    这题需要把懒标记写成d * x + y,原数据是d,先乘上x再加y(x的缺省值是1,y是0)。

    如果有一个操作要加上p,那么就有:
    -> (d * x + y) + p
    -> d * x + (y + p)
    -> d * x + y' (y' = y + p)
    如果有一个操作要乘以q,那么就有:
    -> (d * x + y) * q
    -> d * x * q + y * q
    -> d * x' + y' (x' = x * q, y' = y * q)
    这样就能把信息结合起来了。
    当然,你也可以用(d + x) * y的形式,可以算一下,是可以的,问题就在于有除法,这个不仅运算慢,而且精度不高。


下面是代码(1是乘法,2是加法,3是询问):

#include <cstdio>
#include <iostream>
using namespace std;

#define N 100007

#define NODE 262147
long long P, sum[NODE], dem[NODE], dep[NODE];

void build(int root, int lo, int hi) 
{
    if (lo == hi) scanf("%I64d", &sum[root]);
    else 
    {
        int mi = (lo + hi) >> 1, L = root << 1, R = L | 1;;
        build(L, lo, mi);
        build(R, mi + 1, hi);
        sum[root] = (sum[L] + sum[R]) % P;
        dem[root] = 1LL;
    }
}

void down(int root, int lo, int hi) 
{
    int mi = (lo + hi) >> 1, L = root << 1, R = L | 1;
    
    dem[L] = (dem[L] * dem[root]) % P;
    dem[R] = (dem[R] * dem[root]) % P;
    dep[L] = (dep[L] * dem[root] + dep[root]) % P;
    dep[R] = (dep[R] * dem[root] + dep[root]) % P;
    
    sum[L] = (sum[L] * dem[root] + dep[root] * (mi - lo + 1)) % P;
    sum[R] = (sum[R] * dem[root] + dep[root] * (hi - mi)) % P;
    dem[root] = 1LL;
    dep[root] = 0LL;
}

void modify(int root, int lo, int hi, int x, int y, long long c, bool t) 
{
    if (y < lo || x > hi) return;
    if (x <= lo && y >= hi)
        if (t) 
        {
            dem[root] = (dem[root] * c) % P;
            dep[root] = (dep[root] * c) % P;
            sum[root] = (sum[root] * c) % P;
        }
        else 
        {
            dep[root] = (dep[root] + c) % P;
            sum[root] = (sum[root] + c * (hi - lo + 1)) % P;
        }
    else 
    {
        int mi = (lo + hi) >> 1, L = root << 1, R = L | 1;
        down(root, lo, hi);
        modify(L, lo, mi, x, y, c, t);
        modify(R, mi + 1, hi, x, y, c, t);
        sum[root] = (sum[L] + sum[R]) % P;
    }
}

long long ask(int root, int lo, int hi, int x, int y) 
{
    if (y < lo || x > hi) return 0;
    if (x <= lo && y >= hi) return sum[root];
    int mi = (lo + hi) >> 1, L = root << 1, R = L | 1;
    down(root, lo, hi);
    return (ask(L, lo, mi, x, y) + ask(R, mi + 1, hi, x, y)) % P;
}

int main() 
{
    freopen("seq.in", "r", stdin);
    freopen("seq.out", "w", stdout);
    
    int n;
    scanf("%d%I64d\n", &n, &P);
    
    build(1, 0, n - 1);
    
    int m;
    scanf("\n%d", &m);
    while (m --) {
        int t, lo, hi, c;
        scanf("\n%d%d%d", &t, &lo, &hi);
        lo --, hi --;
        
        if (t < 3) {
            scanf("%d", &c);
            modify(1, 0, n - 1, lo, hi, (long long) c, t == 1);
        }
        else cout << ask(1, 0, n - 1, lo, hi) << endl;
    }
    
    return 0;
}


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值