VJ Can you answer these queries ? (线段树区间修改+区间查询+剪枝)

原题
这题是一道很坑很坑的题!
题目给的是区间两个端点,并没说谁大谁小,所以在 change 之前要注意 l 和 r 大小,保证l <= r;
还有就是如果区间内的所有数都是 1 的话就不需要再修改了,否则再递归下去就会超时。所以要剪枝,当r(p)-l(p)+1==sum(p)时,就可以 return 掉了;

AC 代码:

#include<bits/stdc++.h>
using namespace std;
typedef long long ll;
ll n,m;
ll a[1000005];
struct tree
{
   ll l,r;
   ll sum;
   #define l(x) tree[x].l
   #define r(x) tree[x].r
   #define sum(x) tree[x].sum
}tree[4*200000+10];
void build(ll p,ll l,ll r)
{
    l(p)=l,r(p)=r;
   if(l==r)
   {
     sum(p)=a[l];
     return ;
   }
   ll mid=l+r>>1;
   build(2*p,l,mid);
   build(2*p+1,mid+1,r);
   sum(p)=sum(2*p)+sum(2*p+1);
}
void change(ll p,ll l,ll r)
{
   if(sum(p)==r(p)-l(p)+1)
    return ;
   if(l(p)==r(p))
   {
     sum(p)=sqrt(sum(p));
     return ;
   }
   ll mid=(l(p)+r(p)>>1);
   if(l<=mid)
    change(2*p,l,r);
   if(r>mid)
    change(2*p+1,l,r);
    sum(p)=sum(2*p)+sum(2*p+1);
}
ll ask(ll p,ll l,ll r)
{
   if(l<=l(p)&&r(p)<=r)
    return sum(p);
   ll mid=l(p)+r(p)>>1;
     ll ans=0;
   if(l<=mid)
    ans+=ask(2*p,l,r);
   if(mid<r)
    ans+=ask(2*p+1,l,r);
   return ans;
}
int main()
{
     ll cot=1;
    while(~scanf("%lld",&n))
     {
         printf("Case #%lld:\n",cot++);
        for(ll i=1;i<=n;i++)
        {
          scanf("%lld",&a[i]);
        }
        build(1,1,n);
        scanf("%lld",&m);
        while(m--)
        {
          ll p,l,r;
          scanf("%lld%lld%lld",&p,&l,&r);
          ll x=l+r;
          l=min(l,r);
          r=x-l;
          if(p==0)
            change(1,l,r);
          else if(p==1)
            printf("%lld\n",ask(1,l,r));
        }
        printf("\n");
     }
return 0;
}

posted @ 2020-03-11 22:09  Pecoz  阅读(125)  评论(0编辑  收藏  举报