Efficient Range Operations with Square Root Updates Using Fenwick Tree
Problem Overview
This problem involves handling range queries and updates where certain operations require taking square roots of elements within specified ranges. The solution leverages a Binary Indexed Tree (Fenwick Tree) combined with a set data structure for optimal performance.
Core Strategy
The approach combines:
- A Fenwick Tree for efficient range sum queries
- A set container to track positions containing values greater than 1
- Optimized square root operations that only process relevant elements
Since square root operations quickly reduce values toward 1, we maintain a set of indices that still contain values > 1, avoiding unnecessary computations on elements that have already stabilized.
Impleemntation Details
#include <iostream>
#include <set>
#include <cmath>
using namespace std;
typedef long long ll;
const int MAXN = 100005;
ll n, m, operation, left_idx, right_idx;
ll values[MAXN], tree[MAXN];
set<ll> active_positions;
ll get_lowbit(ll x) {
return x & (-x);
}
ll prefix_sum(ll pos) {
ll result = 0;
for (ll i = pos; i > 0; i -= get_lowbit(i)) {
result += tree[i];
}
return result;
}
void modify_tree(ll pos, ll delta) {
for (ll i = pos; i <= n; i += get_lowbit(i)) {
tree[i] += delta;
}
}
int main() {
ios_base::sync_with_stdio(false);
cin.tie(nullptr);
cin >> n;
for (ll i = 1; i <= n; i++) {
cin >> values[i];
modify_tree(i, values[i]);
if (values[i] > 1) {
active_positions.insert(i);
}
}
cin >> m;
while (m--) {
cin >> operation >> left_idx >> right_idx;
if (left_idx > right_idx) {
swap(left_idx, right_idx);
}
if (operation == 1) { // Range sum query
cout << prefix_sum(right_idx) - prefix_sum(left_idx - 1) << "\n";
} else { // Square root update operation
auto it = active_positions.lower_bound(left_idx);
while (it != active_positions.upper_bound(right_idx)) {
ll current_pos = *it;
modify_tree(current_pos, -values[current_pos]);
values[current_pos] = static_cast<ll>(sqrt(values[current_pos]));
modify_tree(current_pos, values[current_pos]);
if (values[current_pos] <= 1) {
it = active_positions.erase(it);
} else {
++it;
}
}
}
}
return 0;
}
Key Optimization
The critical insight is that square root operations converge rapid. After several aplications, most numbers become 1 and remain unchanged under further square root operations. The set maintains only those positions requiring future updates, dramatically reducing computational overhead.
For each update operation, we iterate through the intersection of the query range and our active set, applying square root transformations and updating both the Fenwick Tree and our tracking set accordingly.