【题解】P4145
考试的 D 题。
题意
有 \(n\) 个数和 \(m\) 个操作,每次操作有三个整数 \(k,l,r\)
- k = 0:给 \([l,r]\) 中的每个数开平方根(向下取整)
- k = 1:询问 \([l,r]\) 中各个数的和。
思路
标签:并查集、分块???不会……
我们用朴素的线段树。但如果直接暴力修改会 T 飞掉:Link(竟然还有40分)
我们需要剪枝!
可以发现若 \(n\le1\),则 \([\sqrt{n}]=[n]\),相当于这一部分不用处理。利用这个性质算法可以得到显著的优化,我们记录 \(maxx\) 为子树中的最大值,若修改时 \(maxx\le1\) 则忽略。
具体细节见代码。
\(\text{Code}\)
(记得开 long long
)
#include <iostream>
#include <cstdio>
#include <cmath>
#define int long long
#define lson pos << 1
#define rson pos << 1 | 1
using namespace std;
const int MAXN = 1e5 + 5;
int n, m;
struct tree
{
int l, r, sum, maxx;
}t[MAXN << 2];
int max(int x, int y)
{
if (x >= y)
{
return x;
}
return y;
}
void pushup(int pos)
{
t[pos].sum = t[lson].sum + t[rson].sum;
t[pos].maxx = max(t[lson].maxx, t[rson].maxx);
}
void build(int l, int r, int pos)
{
t[pos].l = l, t[pos].r = r;
if (l == r)
{
scanf("%lld", &t[pos].sum);
t[pos].maxx = t[pos].sum;
return;
}
int mid = (l + r) >> 1;
build(l, mid, lson);
build(mid + 1, r, rson);
pushup(pos);
}
void update(int L, int R, int pos)
{
int l = t[pos].l, r = t[pos].r;
if (l == r) //到叶子节点时修改
{
t[pos].sum = sqrt(t[pos].sum);
t[pos].maxx = sqrt(t[pos].maxx);
return;
}
int mid = (l + r) >> 1;
if (L <= mid && t[lson].maxx > 1) //大于1时修改
{
update(L, R, lson);
}
if (R > mid && t[rson].maxx > 1)
{
update(L, R, rson);
}
pushup(pos);
}
int query(int L, int R, int pos)
{
int l = t[pos].l, r = t[pos].r;
if (l >= L && r <= R)
{
return t[pos].sum;
}
int mid = (l + r) >> 1, res = 0;
if (L <= mid)
{
res = query(L, R, lson);
}
if (R > mid)
{
res += query(L, R, rson);
}
return res;
}
signed main()
{
scanf("%lld", &n);
build(1, n, 1);
scanf("%lld", &m);
while (m--)
{
int k, l, r;
scanf("%lld%lld%lld", &k, &l, &r);
if (l > r) //细节!!!
{
swap(l, r);
}
if (k) //k = 1
{
printf("%lld\n", query(l, r, 1));
}
else //k = 0
{
update(l, r, 1);
}
}
return 0;
}