洛谷 P1438 无聊的数列 题解

题目链接:洛谷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题解,在此鸣谢。

感谢大家阅读,我们下次再见(看在作者努力修改格式的面上,能不能给个关注啊)。

相关推荐
千里马-horse13 小时前
在android中 spdlog库的log如何在控制台上输出
android·c++·spdlog
微波仿真13 小时前
实现多通道ADC多次测量取平均值,使用DMA
算法
余俊晖13 小时前
多模态文档理解视觉token剪枝思路
人工智能·算法·剪枝·多模态
aramae14 小时前
详细分析平衡树--红黑树(万字长文/图文详解)
开发语言·数据结构·c++·笔记·算法
再卷也是菜14 小时前
C++篇(13)计算器实现
c++·算法
CHEN5_0214 小时前
【leetcode100】和为k的子数组(两种解法)
java·数据结构·算法
Codeking__14 小时前
DFS算法原理及其模板
算法·深度优先·图论
Victory_orsh15 小时前
“自然搞懂”深度学习系列(基于Pytorch架构)——01初入茅庐
人工智能·pytorch·python·深度学习·算法·机器学习
88号技师15 小时前
2025年8月SCI-汉尼拔·巴卡优化算法Hannibal Barca optimizer-附Matlab免费代码
开发语言·人工智能·算法·数学建模·matlab·优化算法
_w_z_j_15 小时前
C++----变量存储空间
开发语言·c++