Problem:acm.hdu.edu.cn/showproblem.php?pid=4027
题意:给一列值,每次操作要么更新都使得区间 [ left , right ] 内的每一个值都变成原来的平方根(再向下取整);要么询问区间 [ left , right ] 的值的总和。
分析:这种更新不像以前见的成段更新,因为区间内每个值的变化都不一样。
考虑到:1开方之后还是1,所以一个区间如果所有值都小于等于1(没说一定大于0…但按题意应该是吧…)的时侯就不用再往下更新。
题目说总和不超过 2^63,就算 2^64,最多开 7 次方就到 1(所以是不会太耗时的意思吧?)
Source code
#include <cstdio>
#include <cmath>
#include <algorithm>
using namespace std;
const int N = 100000;
long long tree[N+7<<2], hp[N+1];
bool stop[N+7<<2]; // 为true表示不用再往下更新
void pushup(int x)
{
tree[x] = tree[x<<1] + tree[x<<1|1];
// 如果两个子结点都不用往下更新,那此结点也不用
stop[x] = stop[x<<1] && stop[x<<1|1];
}
void build(int l, int r, int id)
{
if(l == r)
{
tree[id] = hp[l];
// 只要小于等于 1 就不用再往下更新
stop[id] = hp[l] < 2LL;
return;
}
int m = l + r >> 1;
build(l, m, id<<1);
build(m+1, r, id<<1|1);
pushup(id);
}
void update(int ul, int ur, int l, int r, int id)
{
if(stop[id]) return;
if(l == r)
{
tree[id] = (long long)sqrt(1.0 * tree[id]);
// 只要小于等于 1 就不用再往下更新
stop[id] = tree[id] < 2LL;
return;
}
int m = l + r >> 1;
if(ul <= m)
update(ul, ur, l, m, id<<1);
if(ur > m)
update(ul, ur, m+1, r, id<<1|1);
pushup(id);
}
long long query(int ql, int qr, int l, int r, int id)
{
if(ql <= l && r <= qr)
return tree[id];
if(r < ql || qr < l)
return 0LL;
int m = l + r >> 1;
return query(ql, qr, l, m, id<<1) +
query(ql, qr, m+1, r, id<<1|1);
}
int main()
{
int n, kase = 0;
while(~scanf("%d", &n))
{
for(int i = 1; i <= n; ++i)
scanf("%I64d", hp + i);
build(1, n, 1);
printf("Case #%d:\n", ++kase);
int m;
scanf("%d", &m);
for(int x,y,t; m--; )
{
scanf("%d%d%d", &t, &x, &y);
if(x > y)
swap(x, y);
if(t == 1)
printf("%I64d\n", query(x, y, 1, n, 1));
else
update(x, y, 1, n, 1);
}
putchar('\n');
}
return 0;
}