ICode9

精准搜索请尝试: 精确搜索
首页 > 其他分享> 文章详细

线段树维护不可合并信息

2021-04-11 21:35:00  阅读:203  来源: 互联网

标签:10 线段 合并 数都 给定 区间 维护 开方 define


前言

这个专题有点意思

这类题目表面上是对区间进行修改操作

但是却又不能标记下传实现问题,这个时候就是不要追求每次的时间复杂度为\(logn\)

只要均摊时间复杂度即可

例题代码

题目大意

你有一个长度为 \(n\) 的序列\(A\),里面每个数都是正数,且总和小于等于\(10^{18}\)

接下来你要在这个序列上做 \(m\) 个操作

  • 给定 \(x,y\)你需要把下标在$ [x, y]$ 中的数都开方(向下取整)
  • 给定 \(x,y\)询问 $A[x] + … + A[y] $
  • $ n,m \le 100000$

题目思路

注意到这题中,除了区间开方,没有要求区间修改。 也就是说,每个数都在不断变小。 就算是\(10^{18}\),经过

\(7\)次开方,也会变成\(1\). 因此,很多数在开方了几次之后,全都会变成 \(1\).

则表面上是区间修改,其实是直接修改单点修改,只要区间和等于\(r-l+1\),那么直接不要继续递归

代码

#include<bits/stdc++.h>
#define fi first
#define se second
#define debug cout<<"I AM HERE"<<endl;
using namespace std;
typedef long long ll;
const int maxn=1e5+5,inf=0x3f3f3f3f,mod=1e9+7;
const double eps=1e-6;
int n,m,q;
ll a[maxn];
ll tree[maxn<<2];
int tot;
void update(int node,int l,int r,int ql,int qr){
    if(tree[node]==r-l+1){
        return ;
    }
    if(l==r){
        tree[node]=sqrt(tree[node]);
        return ;
    }
    int mid=(l+r)/2;
    if(mid>=ql) update(node<<1,l,mid,ql,qr);
    if(mid<qr) update(node<<1|1,mid+1,r,ql,qr);
    tree[node]=tree[node<<1]+tree[node<<1|1];
}
ll query(int node,int l,int r,int ql,int qr){
    if(ql<=l&&r<=qr){
        return tree[node];
    }
    int mid=(l+r)/2;
    ll sum=0;
    if(mid>=ql) sum+=query(node<<1,l,mid,ql,qr);
    if(mid<qr)  sum+=query(node<<1|1,mid+1,r,ql,qr);
    return sum;
}
void build(int node,int l,int r){
    if(l==r){
        tree[node]=a[l];
        return ;
    }
    int mid=(l+r)/2;
    build(node<<1,l,mid);
    build(node<<1|1,mid+1,r);
    tree[node]=tree[node<<1]+tree[node<<1|1];
}

signed main(){
    while(scanf("%d",&n)!=-1){
        printf("Case #%d:\n",++tot);
        for(int i=1;i<=n;i++){
            scanf("%lld",&a[i]);
        }
        build(1,1,n);
        scanf("%d",&m);
        for(int i=1,op,l,r;i<=m;i++){
            scanf("%d%d%d",&op,&l,&r);
            if(l>r) swap(l,r);
            if(op==0){
                update(1,1,n,l,r);
            }else{
                printf("%lld\n",query(1,1,n,l,r));
            }
        }
    }
    return 0;
}

改编题

你有一个长度为 \(n\)的序列 \(A\),里面每个数都是正数,且总和小于等于 \(10^{18}\),

接下来你要在这个序列上做 \(m\)个操作

  • 给定 \(x,y,m\)你需要把下标在$ [x, y] \(中的数都对\) m$ 取模。
  • 给定 \(x,y\)询问 \(A[x] + … + A[y]\; n, m \le 100000\)

改编题思路

本质上一样,根据数学性质,取模后的值最少变为原值的一半

那么操作次数也很少,维护区间最大值即可

如果区间最大值比模数小,则不要需要递归

标签:10,线段,合并,数都,给定,区间,维护,开方,define
来源: https://www.cnblogs.com/hunxuewangzi/p/14645574.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有