codevs2492上帝造题的七分钟 2(线段树)

/*
区间修改 区间查询 可以用线段树搞
但是一般的标记下放对这个题好像不合适 
只能改叶子 然后更新父亲(虽然跑的有点慢)
小优化:如果某个点是1 就不用再开方了
所以搞一个f[i]标记 i 这个点还需不需要处理下去 
注意用longlong 还有就是数组开大 
*/
#include<iostream>
#include<cstdio>
#include<cmath>
#include<cstring>
#define LL long long
#define maxn 500010
using namespace std;
LL n,m,num,x,y,z;
LL a[maxn],f[maxn];
struct node
{
    LL o;
    LL l,r;
    LL lc,rc;
    LL sum;
}t[maxn*2];
LL init()
{
    LL x=0;
    char s;
    bool f=0;
    s=getchar();
    while(s<'0'||s>'9')
      {
          if(s=='-')f=1;
          s=getchar();
      }
    while(s>='0'&&s<='9')
      {
          x=x*10+s-'0';
          s=getchar();
      }
    if(f==0)return x;
    else return -x;
}
void Build(LL ll,LL rr)
{
    LL k=++num;
    t[k].o=k;
    t[k].l=ll;t[k].r=rr;
    if(ll!=rr-1)
      {
          t[k].lc=num+1;
          Build(ll,(ll+rr)/2);
          t[k].rc=num+1;
          Build((ll+rr)/2,rr);
          t[k].sum=t[t[k].lc].sum+t[t[k].rc].sum;
      }
    else t[k].sum=a[ll];
}
void update(LL k,LL ll,LL rr)
{    
    if(f[k])return;//如果是1 说明儿子(如果有的话)要么是1 要么是0 不需要处理 直接返回 
    if(ll==rr-1)
      {
          t[k].sum=sqrt(t[k].sum);
          if(t[k].sum==1)f[k]=1;
          return;
      }
    LL mid=(ll+rr)/2;
    if(y<mid)update(t[k].lc,ll,(ll+rr)/2);
    if(z>=mid)update(t[k].rc,(ll+rr)/2,rr);
    t[k].sum=t[t[k].lc].sum+t[t[k].rc].sum;
    f[k]=f[t[k].lc]&&f[t[k].rc];//如果左右儿子都是1 那么父亲点也不用更新了. 
}
LL find(LL k,LL ll,LL rr)
{
    if(ll<=t[k].l&&rr>=t[k].r)return t[k].sum;
    LL ans=0;
    if(ll<(t[k].l+t[k].r)/2)ans+=find(t[k].lc,ll,rr);
    if(rr>(t[k].l+t[k].r)/2)ans+=find(t[k].rc,ll,rr);
    t[k].sum=t[t[k].lc].sum+t[t[k].rc].sum;
    return ans;
}
int main()
{
    n=init();
    for(int i=1;i<=n;i++)
      a[i]=init();
    Build(1,n+1);
    m=init();
    for(LL i=1;i<=m;i++)
      {
          x=init();
          y=init();
          z=init();
          if(y>z)swap(y,z);
          if(x==0)update(1,1,n+1);
          if(x==1)printf("%lld\n",find(1,y,z+1));
      }
}

 

转载于:https://www.cnblogs.com/yanlifneg/p/5463514.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值