题解 P1438 【无聊的数列】
受 @ Okkkkkkkkk 的启发,特写一篇详细的分块题解
由等差数列的数学性质可知:几段等差数列叠加起来一定也是等差数列.
那么对于区间叠加的等差数列,我们天真地想:
要是每一段都是重合的就好了,暴力加,就不会出现有的地方有等差数列a,有的地方有等差数列b的乱七八糟的情况.
But there is no such thing as a free lunch.
但我们强制实现这个想法:把序列分块,对于每一次区间加,暴力维护两端两个根号块内的序列,对于被修改区间完全包含的块块,我们记录首项和公差,就可以实现这个天真的想法了\= ̄ω ̄=/.
ps: 两端的"散户"要特判在一个块的情况
代码如下:
#include<bits/stdc++.h>
#define N 100001
using namespace std;
int read(){
int data=0,w=1;char ch=getchar();
while(ch!='-'&&(ch<'0'||ch>'9'))ch=getchar();
if(ch=='-')w=-1,ch=getchar();
while(ch<='9'&&ch>='0')data=data*10+ch-48,ch=getchar();
return data*w;
}
const int blk=350;
int a[N],n,m,pos[N],tag[blk],s[blk];
void add(int l,int r,int k,int d){
for(int i=l;i<=min(r,(pos[l]+1)*blk);i++)a[i]+=k+(i-l)*d;
if(pos[l]!=pos[r])
for(int i=(pos[r])*blk+1;i<=r;i++)a[i]+=k+(i-l)*d;//+1
for(int i=pos[l]+1;i<pos[r];i++)tag[i]+=d,s[i]+=k+(i*blk+1-l)*d;//**i*blk
}
int main(){
n=read();m=read();
for(int i=1;i<=n;i++){a[i]=read();pos[i]=(i-1)/blk;}
for(;m;m--)
if(read()&1){
int l=read(),r=read(),k=read(),d=read();
add(l,r,k,d);
}
else{
int q=read();printf("%d\n",a[q]+s[pos[q]]+tag[pos[q]]*(q-((pos[q])*blk+1)));
}
return 0;
}