[Ynoi2011]初始化
题目传送门
题意
- 给定一个序列,支持区间求和和跳点加(即对于 \(y+kx(k \in N)\) 位置上加)
Sol
第一道完全没有提示做出的 Ynoi。
考虑到题目和 P3396 哈希冲突 有点像,不如先看一下那一题题解吧。
获得灵感了吗?
对于修改操作,我们仍然分两类。
- \(x < \sqrt n\)
同样考虑记录 \(f_{i}{j}\) 表示 \(x=i\),\(y=j\) 的修改总和。
我们考虑统计答案。
答案即为 \(\sum\limits_{i=l}^r a_i+\sum\limits_{i=1}^{\sqrt n}\sum\limits_{j=1}^i (f_{i,j}\times(\left\lceil \dfrac{r-j}{i}\right\rceil-\left\lceil \dfrac{l-1-j}{i}\right\rceil))\)。
显然可以分开算后用前缀和优化掉一个 \(\sqrt n\)。
用 \(s_{i,j}\) 表示 \(f_{i,j}\) 对 \(j\) 一维取前缀和,即 \(s_{i,j}=\sum\limits_{k=1}^j f_{i,k}\)。
答案即可表示为 \(\sum\limits_{i=l}^r a_i+\sum\limits_{i=1}^{\sqrt n}((s_{i,i}\times(\left\lfloor \dfrac{r}{i}\right\rfloor-\left\lfloor \dfrac{l-1}{i}\right\rfloor))+s_{i,r\%i}-s_{i,(l-1)\%r})\)。
事实上这个意义比上式更好理解。(
修改处理 \(s_{i,j}\) 即可。
- \(x \ge \sqrt n\)
暴力修改即可。
如上式,考虑迅速求 \(\sum\limits_{i=l}^r a_i\),那么考虑分块。
总复杂度 \(O((n+m)\sqrt n)\)。
记得开 \(\text{long long}\)
\(\text{Code}\)
// wish to get better qwq
#include<bits/stdc++.h>
#define re register int
#define pb push_back
using namespace std;
typedef long long ll;
template <typename T> void rd(T &x){
ll fl=1;x=0;char c=getchar();
for(;!isdigit(c);c=getchar()) if(c=='-') fl=-fl;
for(;isdigit(c);c=getchar()) x=(x<<3)+(x<<1)+c-'0';
x*=fl;
}
void wr(ll x){
if(x<0) x=-x,putchar('-');
if(x<10) putchar(x+'0');
if(x>9) wr(x/10),putchar(x%10+'0');
}
// ---------- IO ---------- //
const int N=2e5+5,SQ=505,mod=1e9+7;
int n,m,op,l,r,qaq,len,tot;
ll a[N],sum[SQ],s[SQ][SQ];
// s[i][j] 指 x=i [1,j] 中 add 的值
inline int block(int x){return (x-1)/len+1;}
inline int L(int x){return (x-1)*len+1;}
inline int R(int x){return x*len;}
inline void modify(int x,int y,int z){
if(x>=tot){
for(re i=y;i<=n;i+=x) a[i]+=z,sum[block(i)]+=z;
return ;
}
for(re i=y;i<=x;i++) s[x][i]+=z;
// cout<<x<<' '<<y<<' '<<z<<endl;
// for(re i=0;i<=x;i++) cout<<s[x][i]<<' ';cout<<endl;
return ;
}
inline ll query(int l,int r){
ll ans=0;int lb=L(block(l)+1),rb=R(block(r)-1),tmp=block(r)-1;
if(block(l)==block(r)){
for(re i=l;i<=r;i++) ans+=a[i];
}
else{
for(re i=l;i<lb;i++) ans+=a[i];
for(re i=rb+1;i<=r;i++) ans+=a[i];
for(re i=block(l)+1;i<=tmp;i++) ans+=sum[i];
}
// cout<<ans<<endl;
for(re i=1;i<=tot;i++) ans+=s[i][r%i]-s[i][(l-1)%i]+s[i][i]*(r/i-(l-1)/i);
return ans;
}
// ---------- Sqrt & Operations ---------- //
int main(){
// freopen(".in","r",stdin);
// freopen(".out","w",stdout);
rd(n);rd(m);len=(int)sqrt(n);tot=block(n);
for(re i=1;i<=n;i++) rd(a[i]),sum[block(i)]+=a[i];
for(re i=1;i<=m;i++){
rd(op);rd(l);rd(r);
if(op==1) rd(qaq),modify(l,r,qaq);
else wr(query(l,r)%mod),puts("");
}
return 0;
}
// ---------- Main ---------- //