洛谷 P1438 无聊的数列 题解
题目链接:洛谷P1438 无聊的数列
哈喽,大家好,我们又见面啦!
今天我们来看 洛谷P1438 无聊的数列。
看到此题,我们首先考虑的就是暴力、树状数组和线段树(本题解并未讲解线段树)。
首先我们可以尝试暴力。
暴力得分:91 分(还不错,开 O2 + 快读 + C++98)
#include<bits/stdc++.h>
#define int long long
using namespace std;
int read(){int x=0,f=1;char ch=getchar();while(!isdigit(ch)){if(ch=='-') f=-1;ch=getchar();}while(isdigit(ch)){x=(x<<1)+(x<<3)+(ch^48);ch=getchar();}return x*f;
}
const int N=1e6+10;
int n=read(),m=read(),op,x,y,z,w,a[N];
signed main(){for(int i=1;i<=n;i++){a[i]=read();}while(m--){op=read();x=read();if(op==1){y=read();z=read();w=read();for(int i=x;i<=y;i++){a[i]+=z;z+=w;}}else{printf("%d\n",a[x]);}}return 0;
}
所以,此题我们不能单纯用暴力,我们要用树状数组!
首先,我们考虑一种比较慢点方法,循环单点修改。
代码很简单,得分:27 分(还没暴力快)。
#include<bits/stdc++.h>
#define int long long
#define lowbit(x) x&(-x)
using namespace std;
const int N=1e7+10;
int n,m,op,x,y,z,w,t,tr[N];
void upd(int i,int x){while(i<=n){tr[i]+=x;i+=lowbit(i);}
}
int getsum(int x){int ans=0;while(x>0){ans+=tr[x];x-=lowbit(x);}return ans;
}
signed main(){cin>>n>>m;for(int i=1;i<=n;i++){cin>>x;upd(i,x);}while(m--){cin>>op>>x;if(op==1){cin>>y>>z>>w;for(int i=x;i<=y;i++){upd(i,z);z+=w;}}else{cout<<getsum(x)-getsum(x-1)<<"\n";}}return 0;
}
于是,现在开始讲正解:
我们用树状数组 + 二阶差分。
下面是推导过程:
首先考虑公差为 0 的情况:
这种情况比较简单,我们可得:
然后,我们就可以得到:
还有一种情况,首项为 0 。相似,我们最终得到:
于是,结果就出来了:
推出式子以后,代码就简单了。
100 分代码(最慢点 118 ms,还不错):
#include<bits/stdc++.h>
#define int long long
#define lowbit(x) x&(-x)
using namespace std;
const int N=1e7+10;
int n,m,op,x,y,z,w,a[N],b[N],tr[N],tr1[N];
void upd(int x,int y){int tmp=x;while(x<=n){tr[x]+=y;tr1[x]+=y*tmp;x+=lowbit(x);}
}
int getsum(int x){int tmp=x,ans=0;while(x){ans+=(tmp+1)*tr[x]-tr1[x];x-=lowbit(x);}return ans;
}
signed main(){cin>>n>>m;for(int i=1;i<=n;i++){cin>>a[i];b[i]=a[i]-a[i-1];upd(i,b[i]-b[i-1]);}while(m--){cin>>op;if(op==1){cin>>x>>y>>z>>w;upd(x,z);upd(x+1,w-z);upd(y+1,-z-(y-x+1)*w);upd(y+2,z+(y-x)*w);}else{cin>>x;cout<<getsum(x)<<"\n";}}return 0;
}
最后,我们略微看一下线段树的解法:
100 分代码如下(最慢点 81 ms,挺快的):
#include<bits/stdc++.h>
using namespace std;
const int N=1e5+10;
long long tr[N*4],tag[N*4],tag1[N*4];
void pu(long long p,long long l,long long r){if(tag[p]||tag1[p]){long long mid=(l+r)>>1;tr[p<<1]+=tag[p]*(mid-l+1)+tag1[p]*(mid-l+1)*(mid-l)/2;tag[p<<1]+=tag[p];tag1[p<<1]+=tag1[p];long long k1=tag[p]+tag1[p]*(mid-l+1);tr[p<<1|1]+=k1*(r-mid)+tag1[p]*(r-mid)*(r-mid-1)/2;tag[p<<1|1]+=k1;tag1[p<<1|1]+=tag1[p];tag[p]=tag1[p]=0;}
}
void upd(long long t,long long l,long long r,long long l1,long long r1,long long k,long long d){if(l1<=l&&r<=r1){long long cnt=r-l+1;long long tt=k+d*(l-l1);tr[t]+=tt*cnt + d*cnt*(cnt-1)/2;tag[t]+=tt;tag1[t]+=d;return;}pu(t,l,r);long long mid=(l+r)>>1;if(l1<=mid) upd(t<<1,l,mid,l1,r1,k,d);if(r1>mid) upd(t<<1|1,mid+1,r,l1,r1,k,d);tr[t]=tr[t<<1]+tr[t<<1|1];
}
long long qu(long long t,long long l,long long r,long long tmp){if(l==r) return tr[t];pu(t,l,r);long long mid=(l+r)>>1;if(tmp<=mid) return qu(t<<1,l,mid,tmp);else return qu(t<<1|1,mid+1,r,tmp);
}
long long n,m,a,ttt,l,r,k,d;
int main(){ios::sync_with_stdio(false);cin.tie(0);cin>>n>>m;for(long long i=1;i<=n;i++){cin>>ttt;upd(1,1,n,i,i,ttt,0);}while(m--){cin>>a;if(a==1){cin>>l>>r>>k>>d;upd(1,1,n,l,r,k,d);}else{cin>>l;cout<<qu(1,1,n,l)<<'\n';}}return 0;
}
于是,我们就做完了这道题。
后记:推导过程部分学习洛谷 ll_dio 的题解,在此鸣谢。
感谢大家阅读,我们下次再见(看在作者努力修改格式的面上,能不能给个关注啊)。