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;
}

浙公网安备 33010602011771号