Fork me on GitHub

【题解】洋溢着希望

原文链接:https://cnblogs.com/ctjcalc/p/post7.html

题目描述

# 算法分析

前置知识:

  • 线段树
  • 三角恒等变换

这道题除了三角恒等变换,没有什么比较难的地方,如果想了解三角恒等变换,可以看我的这篇博客:【三角学】三角恒等变换公式推导。现在进入正题,本题需要两个公式,即正余弦和角公式

\[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 \\ \]

再看一下题目的要求,我们可以把序列建成一棵线段树,每个结点维护正弦值与余弦值,查询时直接加即可。对于区间加,就用上面的那个公式:

\[\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时计算出标记的正弦值与余弦值,套用上面的公式即可。更新余弦值也是同样的方法。

# 代码实现
#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;
}
posted @ 2020-02-18 11:29  ctjcalc  阅读(237)  评论(0编辑  收藏  举报