题目链接:洛谷P1438 无聊的数列
哈喽,大家好,我们又见面啦!
今天我们来看 洛谷P1438 无聊的数列。
看到此题,我们首先考虑的就是暴力、树状数组和线段树(本题解并未讲解线段树)。
首先我们可以尝试暴力。
暴力 得分:91 分(还不错,开 O2 + 快读 + C++98)
cpp
#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 分(还没暴力快)。
cpp
#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,还不错):
cpp
#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,挺快的):
cpp
#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 的题解,在此鸣谢。
感谢大家阅读,我们下次再见(看在作者努力修改格式的面上,能不能给个关注啊)。