hdu4027题解——线段树
题意:
n个数,m次询问:
0 l r :将[l, r]内的数取根号并向下取整。
1 l r :输出[l, r]区间和。
思路:线段树,维护区间和。
坑点:l 可能会大于 r,需要剪枝,否则直接TLE,当值为1时不需要再取根号了,剪枝后500ms左右。
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
const int N = 1e6 + 10;
const int MAXN = 4 * N;
struct node
{
ll l, r, val;
}t[MAXN];
ll a[N];
void pushup(ll n)
{
t[n].val = t[n * 2].val + t[n * 2 + 1].val;
}
void build(ll n, ll l, ll r)
{
t[n].l = l;
t[n].r = r;
if(l == r)
{
t[n].val = a[l];
return;
}
ll mid = (l + r) / 2;
build(n * 2, l, mid);
build(n * 2 + 1, mid + 1, r);
pushup(n);
}
void update(ll n, ll L, ll R)//区间更新
{
ll l = t[n].l, r = t[n].r;
if(t[n].val == (r - l + 1)) return;//剪枝很重要,但结点为1是不需要再取根号
if(l == r) {
t[n].val = (ll)sqrt(t[n].val);
return ;
}
ll mid = (l + r) / 2;
if(L <= mid) update(n * 2, L, R);
if(R > mid) update(n * 2 + 1, L, R);
pushup(n);
}
void update(ll n, ll pos)//单点更新
{
ll l = t[n].l, r = t[n].r;
if(l == r)
{
t[n].val++;
return ;
}
ll mid = (l + r) / 2;
if(pos <= mid) update(n * 2, pos);
else update(n * 2 + 1, pos);
pushup(n);
}
ll query(ll n, ll L, ll R)//区间查询
{
ll l = t[n].l, r = t[n].r;
if(L <= l && R >= r)
{
return t[n].val;
}
if(l == r) return 0;
ll mid = (l + r) / 2, ans = 0;
if(L <= mid) ans += query(n * 2, L, R);
if(mid < R) ans += query(n * 2 + 1, L, R);
return ans;
}
int main()
{
ll n, m, c = 1;
while (scanf("%lld", &n) != EOF)
{
for (ll i = 1; i <= n; i++)
scanf("%lld", &a[i]);
build(1, 1, n);
scanf("%lld", &m);
ll op, l, r;
printf("Case #%lld:\n", c++);
for (ll i = 1; i <= m; i++)
{
scanf("%lld %lld %lld", &op, &l, &r);
if(l > r) swap(l, r);
if(op)
{
printf("%lld\n", query(1, l, r));
}
else
{
update(1, l, r);
}
}
printf("\n");
}
}