zoukankan      html  css  js  c++  java
  • 【题解】洋溢着希望

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

    题目描述

    # 算法分析

    前置知识:

    • 线段树
    • 三角恒等变换

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

    [S_{(alpha+eta)}:sin(alpha+eta)=sinalphacoseta+cosalphasineta \ C_{(alpha+eta)}:cos(alpha+eta)=cosalphacoseta-sinalphasineta \ ]

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

    [egin{aligned}sum_{i=l}^{r}sin(a_i+v)&=sum_{i=l}^{r}(sin a_icos v+cos a_isin v) \&=sum_{i=l}^{r}sin a_icos v+sum_{i=l}^{r}cos a_isin vend{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
    ", query(1, 1, n, l, r));
            }
        }
        return 0;
    }
    
  • 相关阅读:
    CSDN专栏收集
    Android英文文档翻译系列(5)——VPNService
    Android英文文档翻译系列(4)——PopupWindow
    Android英文文档翻译系列(3)——AsyncTask
    Android英文文档翻译系列(2)——HandlerThread
    Android英文文档翻译系列(1)——AlarmManager
    Apktool源码解析——第二篇
    Apktool源码解析——第一篇
    AndroidのBuild工具之Ant动手实践
    Java的switch是否支持String作为参数,还支持哪些类型?
  • 原文地址:https://www.cnblogs.com/ctjcalc/p/post7.html
Copyright © 2011-2022 走看看