题意:
给定100000个数,两种操作,
0 i j表示将i j这段的数字都开根号(向下取整)
1 i j表示查询i j之间的所有值的和
所有的和都不超过64位
思路:
如果直接用线段树更新会tle,此题的关键是要理解对任何64位以内的值,开根号最多不会超过7次,所以用线段树做,更新到叶子节点的次数最多7次,如果叶子节点已经更新为1了,那么再开根号也不会变了。
#include <cstdio>
#include <cstring>
#include <cmath>
#include <algorithm>
#define lson root<<1, l, mid
#define rson root<<1|1, mid+1, r
using namespace std;
const int INF = 0x3f3f3f3f;
typedef long long LL;
const int maxn = 100000+5;
LL Tree[maxn<<2];
void push_up(int root){
Tree[root] = Tree[root<<1] + Tree[root<<1|1];
}
void Stree_build(int root, int l, int r){
if(l == r){
scanf("%lld", &Tree[root]);
return;
}
int mid = (l+r) >> 1;
Stree_build(lson);
Stree_build(rson);
push_up(root);
}
void update(int la, int rb, int l, int r, int root){
if(la <= l&&rb >= r&&Tree[root] == r-l+1) return; // 剪枝
if(l == r){
Tree[root] = (LL)sqrt(1.0*Tree[root]);
return;
}
int mid = (l+r) >> 1;
if(la <= mid)
update(la, rb, l, mid, root<<1);
if(rb > mid)
update(la, rb, mid+1, r, root<<1|1);
push_up(root);
}
LL ans;
void Query(int la, int rb, int l, int r, int root){
if(la <= l&&rb >= r){
ans+= (LL)Tree[root];
return;
}
if(l == r) return;
int mid = (l+r) >> 1;
if(la <= mid)
Query(la, rb, l, mid, root<<1);
if(rb > mid)
Query(la, rb, mid+1, r, root<<1|1);
push_up(root);
}
int main()
{
freopen("in.txt","r",stdin);
int n, m, op,a,b;
int kase = 1;
while(scanf("%d",&n) == 1&&n){
Stree_build(1, 1, n);
scanf("%d", &m);
printf("Case #%d:\n", kase++);
while(m--){
scanf("%d%d%d", &op,&a,&b);
if(a > b) swap(a, b);
if(0 == op){
update(a, b, 1, n, 1);
}
else{
ans = 0;
Query(a, b, 1, n, 1);
printf("%lld\n", ans);
}
}
printf("\n");
}
return 0;
}