zoukankan      html  css  js  c++  java
  • 线段树维护不可合并信息

    前言

    这个专题有点意思

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

    但是却又不能标记下传实现问题,这个时候就是不要追求每次的时间复杂度为(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:
    ",++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
    ",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)

    改编题思路

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

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

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

    卷也卷不过,躺又躺不平
  • 相关阅读:
    C++11并发——多线程std::thread (一)
    css属性操作
    mustache使用
    layer常用方法代码
    layer使用
    java后台获取和js拼接展示信息
    生成二维码
    循环体中去除一部分特定的数据
    eclipse工具maven项目打包文件不是最新修改的
    sql server数据库备份单个表的结构和数据生成脚本
  • 原文地址:https://www.cnblogs.com/hunxuewangzi/p/14645574.html
Copyright © 2011-2022 走看看