这个题目有两种方法,一种是利用线段树的思想进行标记下传操作,另一种则是经过数学推导使得对块标记不做改变
记 add[] 为对整个块的每一个数都加 add,mul[] 为对整个块内的每一个数都 乘以 mul
标记下传代码:
- 在对整个区间进行操作 x 时,区间加法只需操作 add 数组,区间乘法修改 add=add*x mul=mul*x 这两个数组
- 当对部分区间进行 +x 时,任意一个数 a[i]=a[i]*mul+add ,所以首先进行下放操作,然后修改 a[i] 的值
- 当对部分区间进行 *x 时,任意一个数 a[i]=a[i]*mul+add , 所以还是要对区间进行下放操作,然后修改 a[i] 的值
const ll mod=10007;
const int N=1e5+5;
int i,j,k;
int n,m,t;
int a[N];
int bel[N],block,num,L[N],R[N];
int sum[N],tag[N]; //sum 表示块+,tag 表示块*
void build()
{
block=sqrt(n);
num=n/block; if(n%block) num++;
for(int i=1;i<=num;i++){
int l=(i-1)*block+1;
int r=min(i*block,n);
L[i]=l,R[i]=r;
sum[i]=0,tag[i]=1;
for(int j=l;j<=r;j++) bel[j]=i;
}
}
void rep(int &a)
{
a%=mod;
a+=mod;
a%=mod;
}
void push_down(int x)
{
if(sum[x]==0 && tag[x]==1) return ;
int l=L[x],r=R[x];
for(int i=l;i<=r;i++){
a[i]*=tag[x]; rep(a[i]);
a[i]+=sum[x]; rep(a[i]);
}
tag[x]=1,sum[x]=0;
}
void add(int l,int r,int w)
{
int st=bel[l],en=bel[r];
if(st==en){
push_down(st);
for(int i=l;i<=r;i++) a[i]+=w,rep(a[i]);
} else{
push_down(st);
for(int i=l;i<=R[st];i++) a[i]+=w,rep(a[i]);
push_down(en);
for(int i=L[en];i<=r;i++) a[i]+=w,rep(a[i]);
for(int i=st+1;i<=en-1;i++) sum[i]+=w,rep(sum[i]);
}
}
void mul(int l,int r,int w)
{
int st=bel[l],en=bel[r];
if(st==en){
push_down(st);
for(int i=l;i<=r;i++) a[i]*=w,rep(a[i]);
} else{
push_down(st);
for(int i=l;i<=R[st];i++) a[i]*=w,rep(a[i]);
push_down(en);
for(int i=L[en];i<=r;i++) a[i]*=w,rep(a[i]);
for(int i=st+1;i<=en-1;i++){
tag[i]*=w;
sum[i]*=w;
if(tag[i]%mod) tag[i]%=mod;
sum[i]%=mod;
}
}
}
int query(int pos)
{
int x=bel[pos];
int ans=a[pos]*tag[x]%mod+sum[x];
return ans%mod;
}
int main()
{
//IOS;
while(~sd(n)){
for(int i=1;i<=n;i++) sd(a[i]),a[i]%=mod;
build();
while(n--){
int opt=read(),l=read(),r=read(),w=read();
if(opt==1){ mul(l,r,w); }
else if(opt==2){ pd(query(r)); }
else add(l,r,w);
}
}
//PAUSE;
return 0;
}
数学求解技巧:
在对部分区间进行更新时,每次都要进行下传操作,复杂度过高,能不能不进行操作,而且在修改任意值的时候,同时区间的性质不发生改变呢
- 当对部分区间进行加法操作时,a[i] 实际上为 a[i]*mul+add ,如果在 a[i] 上单独加 x,是无法直接加的,但是将 x 变为 这样就可以直接加入其中了
- --------结束
- 当对部分区间进行乘法操作时,a[i] 实际上为 a[i]*mul+add , 如果在 a[i] 上单独乘以 x,则有
- 但是我们不想让标记下传,也不想要修改区间的性质,所以有
- 求解 y,解释我们想要得到的,,将 y 把 a[i] 替换即可
- --------结束
最后需要注意的是,由于本题涉及求余,所以式子中的所有除法运算都要转换成逆元,由于 mod=10007 ,所以利用费马小定理预处理即可