SP2713 GSS4

「题意」: n 个数,和在10^18 范围内。n<=1e5

现在有「两种」操作

0 x y把区间[x,y] 内的每个数开方,下取整

1 x y询问区间[x,y] 的每个数的和

和平常的线段树一样,只不过多了一个开方,拿计算器算一下,发现1e18开了大概6次后就会变为1,以后在开方就没用了,所以可以直接跳过这个区间

具体来说,维护一个区间的最大值,在每次执行‘ 0 ' 操作时,单点修改,若发现此区间最大值<=1,则不进入此区间,这样可以降低许多复杂度.

Code:

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define ll long long
using namespace std;
int n,m,T;
ll a[100010];
struct Node{
    int l,r;ll dat,sum;
    #define l(p) t[p].l
    #define r(p) t[p].r
    #define sum(p) t[p].sum
}t[100010*4];
inline ll read(){
    char c=getchar();ll x=0,flag=1;
    while(c<'0' || c>'9'){if(c=='-') flag=-1;c=getchar();}
    while(c>='0' && c<='9') x=(x<<1)+(x<<3)+c-'0',c=getchar();
    return x*flag;
}
void build(int p,int l,int r){
    t[p].l=l;t[p].r=r;
    if(l==r){t[p].dat=t[p].sum=a[l];return;}
    int mid=(l+r)>>1;build(p*2,l,mid);build(p*2+1,mid+1,r);
    t[p].dat=max(t[p*2].dat,t[p*2+1].dat);t[p].sum=t[p*2].sum+t[p*2+1].sum;
}
void change(int p,int l,int r){
    if(l(p)==r(p)){t[p].dat=sqrt(t[p].dat);t[p].sum=t[p].dat;return;}
    int mid=(l(p)+r(p))>>1;
    if(l<=mid && t[p*2].dat>1) change(p*2,l,r);
    if(r>mid && t[p*2+1].dat>1) change(p*2+1,l,r);
    t[p].dat=max(t[p*2].dat,t[p*2+1].dat);t[p].sum=t[p*2].sum+t[p*2+1].sum;  
}
ll ask(int p,int l,int r){
    if(l<=l(p) && r>=r(p)) return sum(p);
    int mid=(l(p)+r(p))>>1;ll val=0;
    if(l<=mid) val+=ask(p*2,l,r);if(r>mid) val+=ask(p*2+1,l,r);
    return val;
}
int main(){
    while(scanf("%d",&n) != EOF){
        printf("Case #%d:
",++T);
        memset(&t,0,sizeof(t));
        for(int i=1;i<=n;i++) a[i]=read();
        build(1,1,n);m=read();
        while(m--){
            int num,x,y;num=read();x=read();y=read();
            if(!num) change(1,min(x,y),max(x,y));
            else printf("%lld
",ask(1,min(x,y),max(x,y)));
        }
        printf("
");
    }
}
原文地址:https://www.cnblogs.com/SyhAKIOI/p/11675612.html