题目链接:
题目大意:
很显然,如果只有区间乘法,和分块入门 1 的做法没有本质区别,但要思考如何同时维护两种标记。
我们让乘法标记的优先级高于加法(如果反过来的话,新的加法标记无法处理)
若当前的一个块乘以m1后加上a1,这时进行一个乘m2的操作,则原来的标记变成m1 * m2,a1 * m2
若当前的一个块乘以m1后加上a1,这时进行一个加a2的操作,则原来的标记变成m1,a1+a2
#include<bits/stdc++.h>
using namespace std;
typedef long long LL;
//由块号寻找第一个块元素的下标
#define LL(x) ((x-1)*Len+1)
const int mod=10007;
LL read()
{
LL x=0,f=1;char ch=getchar();
while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();}
return x*f;
}
int a[100005], b[100005], add1[1005], add2[1005];
int Len, n;
void reset(int x)//标记下推
{
for(int i=LL(x);i<min(LL(x+1), n);i++)
{
a[i]=(a[i]*add1[x]+add2[x])%mod;
}
add1[x]=1, add2[x]=0;
}
void Add(int k, int L, int R, int c)
{
int bL=b[L], bR=b[R];
if(bL==bR)
{
reset(bL);
for(int i=L;i<=R;i++)
{
if(k==0)
{
a[i]=(a[i]+c)%mod;
}
else
{
a[i]=(a[i]*c)%mod;
}
}
}
else
{
reset(bL);
for(int i=L;i<LL(bL+1);i++)
{
if(k==0)
{
a[i]=(a[i]+c)%mod;
}
else
{
a[i]=(a[i]*c)%mod;
}
}
for(int i=bL+1; i<bR; i++)
{
if(k==0)
{
add2[i]=(add2[i]+c)%mod;
}
else
{
add1[i]=(add1[i]*c)%mod;
add2[i]=(add2[i]*c)%mod;
}
}
reset(bR);
for(int i=LL(bR);i<=R;i++)
{
if(k==0)
{
a[i]=(a[i]+c)%mod;
}
else
{
a[i]=(a[i]*c)%mod;
}
}
}
}
void build(int n)
{
Len=n/sqrt(n);
for(int i=1;i<=n;i++)
{
a[i]=read();
b[i]=(i-1)/Len+1;
add1[(i-1)/Len+1]=1;
add2[(i-1)/Len+1]=0;
}
}
int main()
{
n=read();
build(n);
for(int i=1;i<=n;i++)
{
int op=read(), L=read(), R=read(), c=read();
if(op==0)//+c
{
Add(op, L, R, c);
}
if(op==1)//*c
{
Add(op, L, R, c);
}
if(op==2)//询问
{
printf("%d\n",(a[R]*add1[(R-1)/Len+1]+add2[(R-1)/Len+1])%mod);
}
}
return 0;
}