SP2713 GSS4

题意

给出一个序列,有两种操作:对[l,r]的数开方(下取整),求[l,r]的区间和

不保证给出的区间[x, y] 有x <= y ,如果x>y,请交换x,y

n,m<=100000

$sum_{i=1}^{n}a_{i} leq 1e18$

题解

上帝造题的7分钟2很早之前写过了,发现双倍经验而且这道题也有点意思,就写了这篇博客。

区间求和+区间修改显然线段树可以,那么怎样区间开方,这个其实是不好维护的。考虑暴力——单点修改,每个点最多被修改10次左右吧就可以变成1就不需要被改了,那我们就需要保证不去不需要被修改的点,这个用区间和与区间长度就可以看出了。

#include<cstdio>
#include<cstring>
#include<cmath>
using namespace std;

#define ls rt<<1
#define rs rt<<1|1
#define mid ((l+r)>>1)

const int maxn=100005;
int n,m,a_l,a_r;
long long sum[maxn<<2],a[maxn];

void swap(int &x,int &y){
  int t=x;
  x=y;
  y=t;
}

void update(int rt){
    sum[rt]=sum[ls]+sum[rs];
}

void build(int rt,int l,int r){
    if(l==r){sum[rt]=a[l];return ;}
    build(ls,l,mid);build(rs,mid+1,r);
    update(rt);
}

void modify(int rt,int l,int r){
    if(sum[rt]<=r-l+1) return;
    if(l==r){sum[rt]=sqrt(sum[rt]);return ;}
    if(a_l<=mid) modify(ls,l,mid);
    if(mid<a_r) modify(rs,mid+1,r);
    update(rt);
}

long long query(int rt,int l,int r){
    if(a_l<=l&&r<=a_r) return sum[rt];
    long long ans=0;
    if(a_l<=mid) ans+=query(ls,l,mid);
    if(mid<a_r) ans+=query(rs,mid+1,r);
    return ans;
}

int  main(){
  int num=0;
    while(scanf("%d",&n)!=EOF){
    printf("Case #%d:
",++num);
    for(int i=1;i<=n;i++) scanf("%lld",&a[i]);
      build(1,1,n);
      scanf("%d",&m);
      for(int i=1;i<=m;i++){
          int ty;
          scanf("%d%d%d",&ty,&a_l,&a_r);
          if(a_l>a_r) swap(a_l,a_r);
          if(ty==0) modify(1,1,n);
          else printf("%lld
",query(1,1,n));
      }
  }
}
View Code
原文地址:https://www.cnblogs.com/sto324/p/11297471.html