两个操作:
0,x,y,表示将区间[x,y]里每个值开平方然后向下取整
1,x,y,区间[x,y]求和
很明显是线段树,可一开始看着开平方不知道怎么下手,分析了下发现其实每个数能被开方的次数不超过6,6以后无论哪个数被开方都只能得到1
所以可以用一个h[o]表示结点o内所有数被开方的相同次数,-1表示区间内次数不同
再用一个f[o]表示当前区间还有多少次开方没有推给子节点
sum[o][i]表示o结点内所有数都被开方i次之后的和,这个是可以预处理掉的
ans[o]就表示结点和了
如果碰到h[o]非-1的话,就ans[o]=sum[o][h[o]],
是-1的话就只能是ans[o] = ans[LEFT(o)] + ans[RIGHT(o)],
而h[o]的计算也很简单,看看它左右子节点的h值是否相同,相同就是该值,否则就是-1
//HDU 4027 562MS 22628K 2608 B G++
#include<cstdio>
#include<cstring>
#include<cmath>
#define LL __int64
#define LEFT(a) ((a)<<1)
#define RIGHT(a) (((a)<<1)|1)
#define MID(a,b) (((a)+(b))>>1)
#define MAXN 400010
LL ans[MAXN], sum[MAXN][7], a[MAXN>>2];
int ct, n, q, t, x, y, l[MAXN], r[MAXN], h[MAXN], f[MAXN];
int getnum(){
int s=0;
char c=getchar();
while(c<48 || c>57) c=getchar();
while(c>=48 && c<=57){
s = s*10+c-48;
c = getchar();
}
return s;
}
LL get64(){
LL s=0;
char c=getchar();
while(c<48 || c>57) c=getchar();
while(c>=48 && c<=57){
s = s*10+c-48;
c = getchar();
}
return s;
}
void cal(int o){
int ll=LEFT(o), rr=RIGHT(o);
for(int i=0; i<7; i++) sum[o][i]=sum[ll][i]+sum[rr][i];
ans[o]=ans[ll]+ans[rr];
}
void build(int o, int ll, int rr){
l[o]=ll;
r[o]=rr;
if(ll<rr){
int m = MID(ll, rr);
build(LEFT(o), ll, m);
build(RIGHT(o), m+1, rr);
cal(o);
}
else{
ans[o]=sum[o][0]=a[ll];
for(int i=1; i<7; i++) sum[o][i] = (LL)(sqrt(sum[o][i-1]));
}
}
void init(){
for(int i=1; i<=n; i++) a[i]=get64();
build(1, 1, n);
}
void maintain(int o){
int ll=LEFT(o), rr=RIGHT(o);
ans[o] = ans[ll]+ans[rr];
if(h[ll]==h[rr]) h[o]=h[ll];
else h[o]=-1;
}
void pushdown(int o);
void set(int o, int ll, int rr, int v);
void pushdown(int o){
int m = MID(l[o],r[o]);
set(LEFT(o), l[o], m, f[o]);
set(RIGHT(o), m+1, r[o], f[o]);
f[o]=0;
}
void set(int o, int ll, int rr, int v){
if(h[o]==6) return;//如果已经开方6次了,就没必要再往下算了
if(l[o]==ll && r[o]==rr){
if(~h[o]){//判断h[o]是否为-1
h[o]+=v;
if(h[o]>6) h[o]=6;
ans[o] = sum[o][h[o]];
f[o]+=v;
}
else{
f[o]+=v;
pushdown(o);
maintain(o);
}
}
else{
if(f[o]) pushdown(o);
int m = MID(l[o], r[o]);
if(m>=rr) set(LEFT(o), ll, rr, v);
else if(m<ll) set(RIGHT(o), ll, rr, v);
else{
set(LEFT(o), ll, m, v);
set(RIGHT(o), m+1, rr, v);
}
maintain(o);
}
}
LL query(int o, int ll, int rr){
if(l[o]==ll && r[o]==rr) return ans[o];
if(f[o]) pushdown(o);
LL tmp;
int m = MID(l[o],r[o]);
if(m>=rr) tmp = query(LEFT(o), ll, rr);
else if(m<ll) tmp = query(RIGHT(o), ll, rr);
else{
tmp = query(LEFT(o), ll, m);
tmp += query(RIGHT(o), m+1, rr);
}
maintain(o);
return tmp;
}
int main(){
ct=0;
while(~scanf("%d", &n)){
printf("Case #%d:\n", ++ct);
memset(f, 0, sizeof(f));
memset(h, 0, sizeof(h));
init();
q=getnum();
while(q--){
t=getnum();
x=getnum();
y=getnum();
if(x>y){
x^=y; y^=x; x^=y;
}
if(t) printf("%I64d\n", query(1, x, y));
else set(1, x, y, 1);
}
puts("");
}
return 0;
}