【BZOJ 1798】维护序列

线段树~
这(D)道(Q)题(S)告诉我说:“你没学过线段树”
嗯……
这题要好好想想……QAQ
来吧


首先要明确的事情是

delta[now]记录的是已经对当前点做过的,但是还没有对当前点的儿子做过的操作

嗯……
我们就这样
慢慢的退一下

标记是给儿子用的

是给儿子用的
奉献精神


然后,对于这个题,我们可以把所有操作统一为对该节点*x +y的形式
void change(int l,int r,LL add,LL mult,int p)
那么区间乘就成了change(l,r,0,v,1);
那么区间加就成了change(l,r,v,1,1);


标记是什么?

对目前的答案进行标记该有的操作之后会得到真实的答案

真理往往隐蔽在众多表象的茫茫迷雾之中,而标记就像穿透这迷雾的明灯,引导着探索者到达真理的彼岸
(old_ans * mult) + add = new_old
标记的作用是引导儿子走上正轨
所以说下放标记的时候要将儿子的ans计算出来

然后怎么更新标记
这成了大问题

{

a = mult
b = add
change(l,r,d,c,1)
x = 更新之后的mult
y = 更新之后的add

}
为了使得
(sum * a + b) * c + d == sum * x + y
sum * a * c + b * c + d == sum * x + y
x = a * c
y = b * c + d
然后就可以更新了~

kill


#include <iostream>
#include <cstdio>
#include <algorithm>
#include <cstring>
#define L(x) (x << 1)
#define R(x) (x << 1 | 1)
#define sz(x) (tree[x].r - tree[x].l + 1)
using namespace std;
const int MAXN = 100000 + 5;
typedef long long LL;
int P;
struct dot
{
    int l,r;
    LL sum,add,mult;
}tree[MAXN << 2];
int num[MAXN];
void update(int p)
{
    tree[p].sum = (tree[L(p)].sum + tree[R(p)].sum) % P;
    return;
}
void build(int l,int r,int p)
{
    tree[p].l = l;
    tree[p].r = r;
    tree[p].mult = 1;
    if(l == r)
    {
        tree[p].sum = num[l];
        return;
    }
    int mid = (l + r) >> 1;
    build(l,mid,L(p));
    build(mid + 1,r,R(p));
    update(p);
    return;
}
void spread(int p)
{
    tree[L(p)].mult = (tree[L(p)].mult * tree[p].mult) % P;
    tree[L(p)].add = ((tree[L(p)].add * tree[p].mult) % P + tree[p].add % P) % P;
    tree[L(p)].sum = ((tree[L(p)].sum * tree[p].mult) % P + (tree[p].add * sz(L(p))) % P) % P;

    tree[R(p)].mult = (tree[R(p)].mult * tree[p].mult) % P;
    tree[R(p)].add = ((tree[R(p)].add * tree[p].mult) % P + tree[p].add % P) % P;
    tree[R(p)].sum = ((tree[R(p)].sum * tree[p].mult) % P + (tree[p].add * sz(R(p))) % P) % P;

    tree[p].add = 0;
    tree[p].mult = 1;
    return;
}
void change(int l,int r,LL add,LL mult,int p)
{
    if(l <= tree[p].l && tree[p].r <= r)
    {
        tree[p].mult = (tree[p].mult * mult) % P;
        tree[p].add = ((tree[p].add * mult) % P + add % P) % P;
        tree[p].sum = ((tree[p].sum * mult) % P + (add * sz(p)) % P) % P;
        return;
    }
    spread(p);
    int mid = (tree[p].l + tree[p].r) >> 1;
    if(l <= mid)change(l,r,add,mult,L(p));
    if(mid <  r)change(l,r,add,mult,R(p));
    update(p);
    return;
}
LL ask(int l,int r,int p)
{
    if(l <= tree[p].l && tree[p].r <= r)
        return tree[p].sum;
    spread(p);
    LL ans = 0;
    int mid = (tree[p].l + tree[p].r) >> 1;
    if(l <= mid)ans += ask(l,r,L(p));
    if(mid <  r)ans += ask(l,r,R(p));
    return ans;
}
int n,m;
int q,a,b;
LL v;
int main()
{
    scanf("%d %lld",&n,&P);
    for(int i = 1;i <= n;i ++)
        scanf("%d",&num[i]);
    build(1,n,1);
    scanf("%d",&m);
    for(int i = 1;i <= m;i ++)
    {
        scanf("%d",&q);
        switch(q)
        {
            case 1:scanf("%d %d %lld",&a,&b,&v);change(a,b,0ll,v,1);break;
            case 2:scanf("%d %d %lld",&a,&b,&v);change(a,b,v,1ll,1);break;
            case 3:scanf("%d %d",&a,&b);printf("%lld\n",ask(a,b,1) % P);break;
        }
    }
    return 0;
}

转载于:https://my.oschina.net/u/2992707/blog/778007

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值