[Luogu P4145] 上帝造题的七分钟2 / 花神游历各国

本文介绍了一种使用分块线段树解决区间内每个数开方及区间求和问题的方法。面对不满足结合律的操作,如开方,文章详细阐述了如何通过单点修改和区间优化策略来处理。通过判断区间的最大开方次数,避免不必要的计算,从而优化时间复杂度。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

题目链接

题目简要:我们需要一个能支持区间内每一个数开方以及区间求和的数据结构。

解题思路:说道区间修改区间查询,第一个想到的当然就是分块线段树。数据范围要用long long。本来我是看到区间这两个字就想着运用一下还不算特别熟的lazy-tag。但是题目是开方嘛。开方不满足结合律,√4+√4≠√8是很显而易见的事情。所以说是不能直接修改sum的。那么只能每个单点修改。

如何单点修改?第一个思考的是for循环一下然后套单点。但是实际上这样时间复杂度会极度的退化,是会超时的。此时我们发现每次单点change的时候会重复的访问包含目标节点的大区间再到最小的叶节点。实际上修改a[i]与a[i+1]就是左右的兄弟节点并且还有共同的父亲。所以我们还是用区间修改的模式。只不过要到叶节点才改。

我们按照上面写的做了以后莫约能拿到四十分左右。那么如何优化呢?我们知道260大概就是长整型的极限了。也就是说一个数最多计算60次的平方根就会到达1。并且再开方也就不会变化。换而言之如果一个区间里面全是1,即sum为区间长度,就可以不用处理了。这里题目有说都为正整数,就不用考虑会有0而不好求是否全为1的情况了。

#include<iostream>
#include<cstdio>
#include<cmath>
#define cm int mid=(l+r)>>1
#define zc k<<1
#define yc (k<<1)+1
#define din l>=z&&r<=y
#define dout r<z||l>y
using namespace std;
long long read(){
    char ch;
    long long res=0,f=1;
    ch=getchar();
    while(ch<'0'||ch>'9'){
        if(ch=='-')f=-1;
        ch=getchar();
    }
    while(ch>='0'&&ch<='9'){
        res=res*10+(ch-'0');
        ch=getchar();
    }
    return res*f;
}
long long n,m,a[1600005],xds_sum[1600005];
void build(int k,int l,int r){
    if(l==r){xds_sum[k]=a[l];return;}
    cm;build(zc,l,mid);build(yc,mid+1,r);
    xds_sum[k]=xds_sum[zc]+xds_sum[yc];
}
void change(int k,int l,int r,int z,int y){
    if(dout)return;
    if(din&&xds_sum[k]==(r-l+1))return;
    if(din&&l==r){xds_sum[k]=(long long)sqrt(xds_sum[k]);return;}
    cm;change(zc,l,mid,z,y);change(yc,mid+1,r,z,y);
    xds_sum[k]=xds_sum[zc]+xds_sum[yc];
}
long long query(int k,int l,int r,int z,int y){
    if(dout)return 0;
    if(din)return xds_sum[k];
    cm;return query(zc,l,mid,z,y)+query(yc,mid+1,r,z,y);
}
int main(){
    n=read();
    for(int i=1;i<=n;++i)a[i]=read();
    build(1,1,n);
    m=read();
    for(int i=1;i<=m;++i){
        int order,x,y;
        order=read();x=read();y=read();
        if(x>y)swap(x,y);
        if(order)printf("%lld\n",query(1,1,n,x,y));
        else change(1,1,n,x,y);
    }
    return 0;
}

 

转载于:https://www.cnblogs.com/clockwhite/p/11208665.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值