题意
给出一个长为 n n 的数列,以及 个操作,操作涉及区间乘法,区间加法,单点询问。
题解
主要考虑如何维护加法和乘法标记。
考虑一般情况,设任意块内中任意元素为
x
x
,所携带的加法标记为,所携带的乘法标记为
m
m
。
- 现在对区间进行加法,加数为,原先的值为
mx+a
m
x
+
a
,进行加法后的值为
mx+a+a′
m
x
+
a
+
a
′
,所以只需要更新加法标记,变为
a+a′
a
+
a
′
。
- 现在对区间进行乘法,乘数为 m′ m ′ ,原先的值为 mx+a m x + a ,进行乘法后的值为 m′mx+am′ m ′ m x + a m ′ ,所以需要更新加法和乘法标记,乘法标记变为 m∗m′ m ∗ m ′ ,加法标记变为 a∗m′ a ∗ m ′ 。
- 对于左右边界块内的部分,首先把所有标记下放,然后暴力更新数据值。
代码
#include<bits/stdc++.h> using namespace std; typedef long long ll; typedef unsigned long long ull; const int nmax = 100000+100; const int INF = 0x3f3f3f3f; const ll MOD = 10007; const int mm = sqrt(1e5+100)+10; ll data[nmax],add[mm],mul[mm]; int L[mm],R[mm],belong[nmax],block,num,n; inline void init(){ block = sqrt(n); num = n / block; if(n % block) num++; for(int i = 1;i<=num;++i){L[i] = (i-1)* block + 1; R[i] = i * block; mul[i] = 1;} R[num] = n; } inline void pushdown(int cur){ if(mul[cur] == 1 && add[cur] == 0) return; for(int i = L[cur];i<=R[cur];++i) data[i] = ((data[i] * mul[cur])%MOD + add[cur]) % MOD; mul[cur] = 1, add[cur] = 0; } int main(){ scanf("%d",&n); init(); for(int i = 1;i<=n;++i){ scanf("%lld",&data[i]); belong[i] = (i-1) / block + 1; } int op,l,r; ll w; for(int j = 1;j<=n;++j){ scanf("%d %d %d %lld",&op,&l,&r,&w); if(op == 0){ pushdown(belong[l]); for(int i = l;i<=min(r,R[belong[l]]);++i) data[i] = (data[i] + w) % MOD; if(belong[r] != belong[l]){ pushdown(belong[r]); for(int i = L[belong[r]];i<=r;++i) data[i] = (data[i] + w) % MOD;} for(int i = belong[l]+1;i<=belong[r]-1;++i) add[i] = (add[i]+w)%MOD; }else if(op == 1){ pushdown(belong[l]); for(int i = l;i<=min(r,R[belong[l]]);++i) data[i] = (data[i] * w)% MOD; if(belong[r] != belong[l]) {pushdown(belong[r]); for(int i = L[belong[r]];i<=r;++i) data[i] = (data[i]*w) % MOD;} for(int i = belong[l]+1;i<=belong[r]-1;++i) mul[i] = (mul[i] *w) %MOD, add[i] = (add[i]*w)%MOD; }else{ ll ans = ((data[r] * mul[belong[r]]) % MOD + add[belong[r]] ) % MOD; printf("%lld\n",ans); } } return 0; }