Luogu SP2713 GSS4 - Can you answer these queries IV

思路

这道题明面上是道紫题,但是洛谷的P4145是和GSS4完全相同的一道题,而P4145是蓝题(众所周知洛谷的评分不太准)。

这道题除了一个区间对每个数开方的操作,其他都与线段树模板无异。那么如何实现区间对每个数开方的操作呢?

很显然,直接想是很难想出来的,而事实上这道题也没有什么可以用的特殊方法,只能暴力对每个数进行开方操作。但是,这是暴力,更何况这道题的时限是500ms(0.5s),这么做能过吗?

题目中的一个条件\(\sum{a_i} \leq 10^{18}\),这个条件可以给我们带来一些启发。考虑最坏的情况,我们对\(10^{18}\)这个数进行暴力开方,结果如下:

第一次, 开方得到1000000000

第二次, 开方得到31622.7766……,按照题目要求下取整,得31622

第三次, 开方得到117.8258……,下取整得117

第四次, 开方得到10.8167……,下取整得10

第五次, 开方得到3.1623……,下取整得3

第六次, 开方得到1.1732……,下取整得1

而我们知道,1这个数的算术平方根是它本身,所以当一个数被开方和下取整到1时,我们就没有必要在对他进行开方操作。而且,上述把\(10^{18}\)这个数开方至1也仅仅用了6步,这样看来暴力完全可行。

但是要通过这道题,我们还需要一些东西。

既然1的算术平方根是它本身,那么如果当一个区间全部为1时,我们就没有必要浪费时间在去对这个区间做开方操作。那么我们怎么能维护一个区间中的数是否都为1呢?

由于开方操作和这道题下取整的要求,这颗线段树中的所有叶子结点所维护的值最小为1。这样的话我们就可以维护一个区间最大值,若区间最大值为1,那么这个区间就肯定全部为1(这个很好理解吧)。

这样的话我们在更新过程中,如果一个区间的最大值为1,那么我们就可以直接跳过,没有必要在对这个区间进行操作。这样的话就可以过这道题了。

Code

#include<iostream>
#include<cstdio>
#include<algorithm>
#include<cmath>
#include<cstring>
#define MAXN 100010
typedef long long ll;
int n, m;
ll a[MAXN];
struct node{
    ll sum, maxx;
} tree[MAXN << 2];//sum 维护区间和,maxx维护区间最大值
inline int lson(int x) { return x << 1; }//求左儿子
inline int rson(int x) { return x << 1 | 1; }//求右儿子
inline void push_up(int k){
    tree[k].sum = tree[lson(k)].sum + tree[rson(k)].sum;//更新区间和
    tree[k].maxx = std::max(tree[lson(k)].maxx, tree[rson(k)].maxx);
    //更新区间最大值
    return;
}
void build(int k,int l,int r){
    if(l==r){//若遍历到叶子结点,直接赋值
        tree[k].sum = a[l];
        tree[k].maxx = a[l];
        return;
    }
    int mid = (l + r) >> 1;
    build(lson(k), l, mid);//建立左子树
    build(rson(k), mid + 1, r);//建立右子树
    push_up(k);//上传
}
void update(int k,int cl,int cr,int l,int r){
    if(l==r){//若遍历到叶子结点,直接开方
        tree[k].sum = std::sqrt(tree[k].sum);
        tree[k].maxx = tree[k].sum;
        return;
    }//这里提一点就是题目要求下取整,我们其实并不用floor这种浪费时间
    //的东西,我们如果用一个int或long long等整型变量来存储double这种
    //浮点数,直接就可以实现下取整操作(注:sqrt函数的返回值为double)
    int mid = (l + r) >> 1;
    if(cl<=mid&&tree[lson(k)].maxx>1)//更新时保证区间不都是为1
        update(lson(k), cl, cr, l, mid);//若都为1不用更新
    if(cr>mid&&tree[rson(k)].maxx>1)//同上
        update(rson(k), cl, cr, mid + 1, r);
    push_up(k);
}
ll query(int k,int ql,int qr,int l,int r){
    ll res = 0;
    if(ql<=l&&r<=qr)
        return tree[k].sum;
    int mid = (l + r) >> 1;
    if(ql<=mid)
        res += query(lson(k), ql, qr, l, mid);
    if(qr>mid)
        res += query(rson(k), ql, qr, mid + 1, r);
    return res;//没啥好解释的了,都是基础操作
}
int main(){
    int cas = 0;
    while(scanf("%d",&n)!=EOF){//读入
        cas++;
        for (int i = 1; i <= n;++i)
            scanf("%lld", &a[i]);
        printf("Case #%d:\n", cas);
        build(1, 1, n);
        scanf("%d", &m);
        for (int i = 1; i <= m;++i){
            int opt = 0;
            int x = 0, y = 0;
            scanf("%d", &opt);
            scanf("%d%d", &x, &y);
            if(x>y)
                std::swap(x, y);//注意要交换
            if(opt==0)
                update(1, x, y, 1, n);
            if(opt==1)
                printf("%lld\n", query(1, x, y, 1, n));
        }
        puts("");
    }
    return 0;
}
posted @ 2020-07-28 10:02  Shadow_hyc  阅读(112)  评论(0)    收藏  举报