SP2713 GSS4 - Can you answer these queries IV(线段树)

传送门

解题思路

  大概就是一个数很少次数的开方会开到\(1\),而\(1\)开方还是\(1\),所以维护一个和,维护一个开方标记,维护一个区间是否全部为\(1/0\)的标记。然后每次修改时先看是否有全\(1\)或\(0\)的标记,有就不用理了,没有就暴力开方。

代码

#include<iostream>
#include<cstdio>
#include<cstring>
#include<cmath>
#define int long long

using namespace std;
const int MAXN = 100005;

inline int rd(){
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {f=ch==‘-‘?0:1;ch=getchar();}
    while(isdigit(ch))  {x=(x<<1)+(x<<3)+ch-‘0‘;ch=getchar();}
    return f?x:-x;
}

int n,m,a[MAXN],sum[MAXN<<2],tag[MAXN<<2],lazy[MAXN<<2],cnt;

void build(int x,int l,int r){
    if(l==r) {
        a[l]=rd();sum[x]=a[l];lazy[x]=tag[x]=0;
        if(sum[x]==1 || sum[x]==0) lazy[x]=1;
        return ;
    }
    int mid=(l+r)>>1;
    build(x<<1,l,mid);build(x<<1|1,mid+1,r);
    sum[x]=sum[x<<1]+sum[x<<1|1];
    lazy[x]=(lazy[x<<1]&lazy[x<<1|1]);tag[x]=0;
}

inline void pushdown(int x,int l,int r){
    int mid=(l+r)>>1;
    if(!lazy[x<<1]){
        tag[x<<1]+=tag[x];lazy[x<<1]=1;sum[x<<1]=0;
        for(int i=l;i<=mid;i++){
            if(a[i]>1) lazy[x<<1]=0;sum[x<<1]+=a[i];
        }
    }
    if(!lazy[x<<1|1]){
        tag[x<<1|1]+=tag[x];lazy[x<<1|1]=0;sum[x<<1|1]=0;
        for(int i=mid+1;i<=r;i++){
            if(a[i]>1) lazy[x<<1|1]=0;sum[x<<1|1]+=a[i];
        }
    }
    tag[x]=0;
}

void update(int x,int l,int r,int L,int R){
    if(lazy[x]) return;
    if(L<=l && r<=R){
        tag[x]++;lazy[x]=1;sum[x]=0;
        for(int i=l;i<=r;i++) {
            if(a[i]>1) a[i]=sqrt(a[i]);
            if(a[i]>1) lazy[x]=0;
            sum[x]+=a[i];
        }
        return ;
    }
    int mid=(l+r)>>1;if(tag[x]) pushdown(x,l,r);
    if(L<=mid) update(x<<1,l,mid,L,R);
    if(mid<R)  update(x<<1|1,mid+1,r,L,R);
    sum[x]=sum[x<<1]+sum[x<<1|1];
    lazy[x]|=(lazy[x<<1]&lazy[x<<1|1]);
}

int query(int x,int l,int r,int L,int R){
    if(L<=l && r<=R) return sum[x];
    int ret=0,mid=(l+r)>>1;
    if(tag[x]) pushdown(x,l,r);
    if(L<=mid) ret+=query(x<<1,l,mid,L,R);
    if(mid<R)  ret+=query(x<<1|1,mid+1,r,L,R);
    return ret;
}

signed main(){
    while(~scanf("%lld",&n)){cnt++;
        printf("Case #%lld:\n",cnt);
        build(1,1,n);m=rd();int op,x,y;
        while(m--){
            op=rd(),x=rd(),y=rd();if(x>y) swap(x,y);
            if(!op) update(1,1,n,x,y);
            else printf("%lld\n",query(1,1,n,x,y));
        }
        putchar(‘\n‘);
    }
    return 0;
}

原文地址:https://www.cnblogs.com/sdfzsyq/p/9977494.html

时间: 2024-08-29 13:36:48

SP2713 GSS4 - Can you answer these queries IV(线段树)的相关文章

SPOJ GSS4 Can you answer these queries IV (线段树)

题目大意: 给出N个数 0     操作   把 l -----  r之间的数全部开平方 1     操作  输出 l -----r  之间的和 思路分析: 判断区间里的数字是否全相同.如果相同, 将cov 置为该数 查询的时候和更新的时候,如果碰到cov != -1 的  就直接返回就可以了 #include <cstdio> #include <iostream> #include <algorithm> #include <cstring> #incl

【SP2713 GSS4 - Can you answer these queries IV】 题解

题目链接:https://www.luogu.org/problemnew/show/SP2713 真暴力啊. 开方你开就是了,开上6次就都没了. #include <cmath> #include <cstdio> #include <cstring> #include <iostream> #include <algorithm> #define ll long long using namespace std; const int maxn

SP2713 GSS4 - Can you answer these queries IV

传送门 \(ZHX\; TQL\) Orz 这道题目我们可以用线段树维护-- 可能有\(dalao\)会问:"线段树怎么维护区间开平方?" 而这道题的精髓就在于,它要我们维护的操作是开平方+下取整.也就是说经过一定的次数,要开平方的数会慢慢缩小为"\(1\)",这个次数是很小的,而\(\sqrt 1=1\). 所以在修改时,我们可以先查询这个区间是否全是"1",如果是,那我们就不管它,再去寻找其他的区间进行修改,如果这个区间里有其他数,我们就将这

hdu4027 Can you answer these queries?(线段树平方减少,区间求和)

转载请注明出处:http://blog.csdn.net/u012860063 题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4027 Problem Description A lot of battleships of evil are arranged in a line before the battle. Our commander decides to use our secret weapon to eliminate the bat

SPOJ GSS3 Can you answer these queries III (线段树)

题目大意: 求区间最大子区间的和. 思路分析: 记录左最大,右最大,区间最大. 注意Q_L  和 Q_R  就好. #include <cstdio> #include <iostream> #include <algorithm> #include <cstring> #define lson num<<1,s,mid #define rson num<<1|1,mid+1,e #define maxn 55555 using na

HDU 4027 Can you answer these queries?(线段树的单点更新+区间查询)

题目链接 题意 : 给你N个数,进行M次操作,0操作是将区间内的每一个数变成自己的平方根(整数),1操作是求区间和. 思路 :单点更新,区间查询,就是要注意在更新的时候要优化,要不然会超时,因为所有的数开几次方之后都会变成1,所以到了1不用没完没了的更新. 1 //HDU 4027 2 #include <cstdio> 3 #include <cstring> 4 #include <cmath> 5 #include <iostream> 6 #defi

SPOJ GSS4 Can you answer these queries IV

Can you answer these queries IV Time Limit: 5000ms Memory Limit: 262144KB This problem will be judged on SPOJ. Original ID: GSS464-bit integer IO format: %lld      Java class name: Main You are given a sequence A of N(N <= 100,000) positive integers.

bzoj 2482: [Spoj GSS2] Can you answer these queries II 线段树

2482: [Spoj1557] Can you answer these queries II Time Limit: 20 Sec  Memory Limit: 128 MBSubmit: 145  Solved: 76[Submit][Status][Discuss] Description 给定n个元素的序列. 给出m个询问:求l[i]~r[i]的最大子段和(可选空子段). 这个最大子段和有点特殊:一个数字在一段中出现了两次只算一次. 比如:1,2,3,2,2,2出现了3次,但只算一次,

HDU 4027 Can you answer these queries(线段树 成段更新)

Problem Description A lot of battleships of evil are arranged in a line before the battle. Our commander decides to use our secret weapon to eliminate the battleships. Each of the battleships can be marked a value of endurance. For every attack of ou