【题解】洋溢着希望
题目描述。
Copyright (C) 2020 ctjcalc,转载请注明URL,并给出原文链接,谢谢。
# 算法分析
前置知识:
- 线段树
- 三角恒等变换
这道题除了三角恒等变换,没有什么比较难的地方,如果想了解三角恒等变换,可以看我的这篇博客:【三角学】三角恒等变换公式推导。现在进入正题,本题需要两个公式,即正余弦和角公式:
\[S_{(\alpha+\beta)}:\sin(\alpha+\beta)=\sin\alpha\cos\beta+\cos\alpha\sin\beta \\
C_{(\alpha+\beta)}:\cos(\alpha+\beta)=\cos\alpha\cos\beta-\sin\alpha\sin\beta \\
\]
再看一下题目的要求,我们可以把序列建成一棵线段树,每个结点维护正弦值与余弦值,查询时直接加即可。对于区间加,就用上面的那个公式:
Copyright (C) 2020 ctjcalc,转载请注明URL,并给出原文链接,谢谢。
\[\begin{aligned}\sum_{i=l}^{r}\sin(a_i+v)&=\sum_{i=l}^{r}(\sin a_i\cos v+\cos a_i\sin v) \\&=\sum_{i=l}^{r}\sin a_i\cos v+\sum_{i=l}^{r}\cos a_i\sin v\end{aligned}
\]
这里的 $ \sum_{i=l}^{r}\sin a_i $ 和 $ \sum_{i=l}^{r}\cos a_i $ 正是线段树结点维护的信息,所以,在pushdown
时计算出标记的正弦值与余弦值,套用上面的公式即可。更新余弦值也是同样的方法。
Copyright (C) 2020 ctjcalc,转载请注明URL,并给出原文链接,谢谢。
# 代码实现
#include <bits/stdc++.h>
using namespace std;
typedef long long ll;
struct node {
double sin, cos;
ll add; // 标记要开 long long
};
constexpr int maxn = 2e5 + 10;
node tree[maxn << 4];
int a[maxn];
int n, m;
inline void pushup(int x) { // 这里直接相加
tree[x].sin = tree[x << 1].sin + tree[x << 1 | 1].sin;
tree[x].cos = tree[x << 1].cos + tree[x << 1 | 1].cos;
}
inline void update(int x, double sinv, double cosv, ll addv) {
tree[x].add += addv;
double sint = tree[x].sin, cost = tree[x].cos;
tree[x].sin = sint * cosv + cost * sinv; // 正弦和角公式
tree[x].cos = cost * cosv - sint * sinv; // 余弦和角公式
}
inline void pushdown(int x) {
if (tree[x].add == 0)
return;
double sinv = sin(tree[x].add), cosv = cos(tree[x].add);
update(x << 1, sinv, cosv, tree[x].add);
update(x << 1 | 1, sinv, cosv, tree[x].add);
tree[x].add = 0;
}
// 下面都是板子了
void modify(int x, int l, int r, int ml, int mr, ll v) {
if (ml <= l && r <= mr) {
double sinv = sin(v), cosv = cos(v);
update(x, sinv, cosv, v);
return;
}
pushdown(x);
int mid = (l + r) >> 1;
if (ml <= mid)
modify(x << 1, l, mid, ml, mr, v);
if (mid < mr)
modify(x << 1 | 1, mid + 1, r, ml, mr, v);
pushup(x);
}
void build(int x, int l, int r) {
if (l == r) {
tree[x].sin = sin(a[l]);
tree[x].cos = cos(a[l]);
return;
}
int mid = (l + r) >> 1;
build(x << 1, l, mid);
build(x << 1 | 1, mid + 1, r);
pushup(x);
}
double query(int x, int l, int r, int ql, int qr) {
if (ql <= l && r <= qr)
return tree[x].sin;
pushdown(x);
int mid = (l + r) >> 1;
double res = 0.0;
if (ql <= mid)
res += query(x << 1, l, mid, ql, qr);
if (mid < qr)
res += query(x << 1 | 1, mid + 1, r, ql, qr);
return res;
}
int main() {
scanf("%d", &n);
for (int i = 1; i <= n; ++i) scanf("%d", &a[i]);
build(1, 1, n);
scanf("%d", &m);
for (int i = 1; i <= m; ++i) {
int op, l, r;
ll v;
scanf("%d%d%d", &op, &l, &r);
if (op == 1) {
scanf("%lld", &v);
modify(1, 1, n, l, r, v);
} else {
printf("%.1lf\n", query(1, 1, n, l, r));
}
}
return 0;
}
作者:ctjcalc
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须在文章页面给出原文连接,否则保留追究法律责任的权利。