【LOJ#6283】数列分块7

题目大意:维护一个 N 个数组成的序列,支持区间加、区间乘、单点询问。

题解:在每一个块中维护两个标记,即:整块加和的标记和整块乘积的标记。不过由于有两个标记,涉及到计算区间总和的顺序问题。
一个指定块的区间加标记为 \(atag\),区间乘标记为 \(mtag\),区间除去标记的和为 \(sum\)
第一种方式:\((sum+atag)*mtag\),第二种方式:\(sum*mtag+atag\)
比如:假设现在区间加标记要增加 \(val\),若采用第一种方式,需要保证 \(atag*mtag=add+val\),显然这需要使得 \(mtag\) 的值改变,因此并不合适。
相反,如果采用第二种方式的话,加法只会改变加法标记,而区间乘法标记改变时,只需将乘法标记同样乘在加法标记上即可,精度符合要求。
综上,可以理解为乘法标记的优先级更高,即:先做乘法运算,后做加法运算。

代码如下

#include <bits/stdc++.h>
using namespace std;
const int maxn=1e5+10;
const int mod=10007;

inline int read(){
    int x=0,f=1;char ch;
    do{ch=getchar();if(ch=='-')f=-1;}while(!isdigit(ch));
    do{x=x*10+ch-'0';ch=getchar();}while(isdigit(ch));
    return f*x;
}

int n,q,tot,pos[maxn];
long long a[maxn];
struct node{
    int l,r;
    long long mul,add;
}b[1000];

void make_block(){
    tot=(int)sqrt(n);
    for(int i=1;i<=tot;i++)b[i].l=(i-1)*tot+1,b[i].r=i*tot;
    if(b[tot].r<n)++tot,b[tot].l=b[tot-1].r+1,b[tot].r=n;
    for(int i=1;i<=tot;i++)
        for(int j=b[i].l;j<=b[i].r;j++)
            pos[j]=i,b[i].mul=1;
}

void read_and_parse(){
    n=read(),q=n;
    for(int i=1;i<=n;i++)a[i]=read();
    make_block();
}

void reset(int x){
    for(int i=b[x].l;i<=b[x].r;i++)a[i]=(a[i]*b[x].mul+b[x].add)%mod;
    b[x].add=0,b[x].mul=1;
}

void modify(int opt,int l,int r,int val){
    int x=pos[l],y=pos[r];
    if(x==y){
        reset(x);
        for(int i=l;i<=r;i++)opt?a[i]*=val:a[i]+=val,a[i]%=mod;
    }else{
        for(int i=x+1;i<=y-1;i++){
            if(opt==0)b[i].add=(b[i].add+val)%mod;
            else b[i].add=(b[i].add*val)%mod,b[i].mul=(b[i].mul*val)%mod;
        }
        reset(x),reset(y);
        for(int i=l;i<=b[x].r;i++)opt?a[i]*=val:a[i]+=val,a[i]%=mod;
        for(int i=b[y].l;i<=r;i++)opt?a[i]*=val:a[i]+=val,a[i]%=mod;
    }
}

void solve(){
    int opt,l,r,val;
    while(q--){
        opt=read(),l=read(),r=read(),val=read();
        if(opt==0)modify(opt,l,r,val);
        else if(opt==1)modify(opt,l,r,val);
        else printf("%lld\n",(a[r]*b[pos[r]].mul+b[pos[r]].add)%mod);
    }
}

int main(){
    read_and_parse();
    solve();
    return 0;
} 

转载于:https://www.cnblogs.com/wzj-xhjbk/p/9976554.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值