传送门:题目
题意:
给一个序列,有两种操作:
- 给区间[l,r],区间的每个值都开根号
- 查询区间[l,r]的sum
题解:
纯的线段树,区间更新,区间求和,套个模板就好。然后有个技巧:
109
10
9
开5次根号就是1了,所以我们在建树或者更新的时候,发现值
≤
≤
1的时候,标记一下,
然后以后更新的时候,遇到已经被标记的点,说明这些点即使再开根号,还是原来的值,所以我们直接跳过这些被标记的点就好了。
坑点:
不能用cin,cout,要不会超时。
AC代码:
#include <iostream>
#include <cstring>
#include <cstdio>
#include <algorithm>
#include <cmath>
#define debug(x) cout<<#x<<" = "<<x<<endl;
#define INF 0x3f3f3f3f
using namespace std;
const int maxn = 1e5 + 10;
int a[maxn];
bool flag[maxn << 2];
/******************线段树模板**********************/
long long SegTree[maxn << 2];
void BuildTree(int l, int r, int rt) {//建树,lr是总区间,rt是根结点一般为1
if (l == r) {
SegTree[rt] = a[l];//初始化叶节点
if (SegTree[rt] <= 1)
flag[rt] = true;
return ;
}
int m = (l + r) >> 1;
BuildTree(l, m, rt << 1);
BuildTree(m + 1, r, rt << 1 | 1);
SegTree[rt] = SegTree[rt << 1] + SegTree[rt << 1 | 1];
flag[rt] = flag[rt << 1] & flag[rt << 1 | 1];
}
long long Query(int L, int R, int l, int r, int rt) {//区间查询,LR是查询区间,lr是总区间,rt是根结点一般为1
if (l >= L && r <= R)
return SegTree[rt];
int m = (l + r) >> 1;
long long ans = 0;
if (L <= m)
ans += Query(L, R, l, m, rt << 1);
if (R > m)
ans += Query(L, R, m + 1, r, rt << 1 | 1);
return ans;
}
void Update(int L, int R, int l, int r, int rt) {
if (flag[rt] == true)
return;
if (l == r) {
SegTree[rt] = (long long)sqrt(SegTree[rt]);
if (SegTree[rt] <= 1)
flag[rt] = true;
return;
}
int m = (l + r) >> 1;
if (R <= m)
Update(L, R, l, m, rt << 1);
else if (L > m)
Update(L, R, m + 1, r, rt << 1 | 1);
else {
Update(L, m, l, r, rt);
Update(m + 1, R, l, r, rt);
}
SegTree[rt] = SegTree[rt << 1] + SegTree[rt << 1 | 1];
flag[rt] = flag[rt << 1] & flag[rt << 1 | 1];
}
/******************线段树模板**********************/
int main(void) {
int n, m, t1, t2, t3;
scanf("%d",&n);
for (int i = 1; i <= n; i++)
scanf("%d",&a[i]);
BuildTree(1, n, 1);
scanf("%d",&m);
while (m--) {
scanf("%d%d%d",&t1,&t2,&t3);
if (t1 == 1)
printf("%lld\n",Query(t2, t3, 1, n, 1));
else if (t1 == 2)
Update(t2, t3, 1, n, 1);
}
return 0;
}