n个数组成的一个序列,下标从1开始。现在共进行m次区间修改和查询工作。0 l r表示[l , r]内每个数开平方向下取整。
1 l r表示对[l ,r]求和。1 <= n <= 100000 , 1 <= m <= 100000。
1.乍一看需要对区间内每个点修改,后来发现每个点最多改6~7次,可以用vis标记这个区间已经全部是1,无需再进行开方操作。
2.接下来就是线段树套路求和了。
#include
#define N 100005
using namespace std ;
int n ;
long long a[N << 2] ;
long long sum[N << 2] ;
bool vis[N << 2] ;
void build(int l , int r , int num)
{
int lson = num * 2 , rson = num * 2 + 1 ;
int mid = (l + r) / 2 ;
if(l == r)
{
sum[num] = a[l] ;
return ;
}
build(l , mid , lson) ;
build(mid + 1 , r , rson) ;
sum[num] = sum[lson] + sum[rson] ;
}
void update(int x , int y , int l , int r , int num)
{
int lson = num * 2 , rson = num * 2 + 1 ;
int mid = (l + r) / 2 ;
if(x > r || y < l || vis[num])
return ;
if(l == r)
{
sum[num] = (long long)(sqrt(sum[num])) ;
if(sum[num] <= 1)
vis[num] = 1 ;
return ;
}
update(x , y , l , mid , lson) ;
update(x , y , mid + 1 , r , rson) ;
sum[num] = sum[lson] + sum[rson] ;
vis[num] = vis[lson] && vis[rson] ;
}
long long query(int x , int y , int l , int r , int num)
{
int lson = num * 2 , rson = num * 2 + 1 ;
int mid = (l + r) / 2 ;
long long ans = 0 ;
if(x > r || y < l)
return 0 ;
if(x <= l && r <= y)
{
return sum[num] ;
}
ans += query(x , y , l , mid , lson) ;
ans += query(x , y , mid + 1 , r , rson) ;
return ans ;
}
int main()
{
int i , j ;
int m ;
int t = 0 ;
int flag , l , r ;
while(scanf("%d" , &n) != EOF)
{
for(i = 1 ; i <= n ; i ++)
scanf("%lld" , &a[i]) ;
memset(vis , 0 , sizeof(vis)) ;
memset(sum , 0 , sizeof(sum)) ;
build(1 , n , 1) ;
scanf("%d" , &m) ;
printf("Case #%d:\n" , ++t) ;
while(m --)
{
scanf("%d%d%d" , &flag , &l , &r) ;
if(l > r)
swap(l , r) ;
if(flag == 0)
update(l , r , 1 , n , 1) ;
else
printf("%lld\n" , query(l , r , 1 , n , 1)) ;
}
printf("\n") ;
}
}