洛谷P6327 区间加区间sin和

题目

https://www.luogu.com.cn/problem/P6327

思路

用线段树维护。

对每个 \([l,r]\) 区间维护两个值:\(f(l,r)=\sum_{i=l}^r sin(x_i)\)\(g(l,r)=\sum_{i=l}^r cos(x_i)\)。区间加就用lazytag处理。

考虑对 \([l,r]\) 区间中每个数加上 \(k\) 会对 \(f\)\(g\) 造成什么影响(设加完之后的区间sin和与区间cos和为 \(f'\)\(g'\) ):

\(f'(l,r)=\sum_{i=l}^r sin(x_i+k)\),用和角公式展开:

\[f'(l,r)=\sum_{i=l}^r sin(x_i)cos(k)+cos(x_i)sin(k)=cos(k)\sum_{i=l}^rsin(x_i)+sin(k)\sum_{i=l}^rcos(x_i)=cos(k)f(l,r)+sin(k)g(l,r) \]

对于\(g'(l,r)\),有:

\[g'(l,r)=\sum_{i=l}^r cos(x_i)cos(k)-sin(x_i)sin(k)=cos(k)\sum_{i=l}^rcos(x_i)-sin(k)\sum_{i=l}^rsin(x_i)=cos(k)g(l,r)-sin(k)f(l,r) \]

所以,我们在lazytag下推的时候就可以直接计算标记影响之后两个子区间的值,pushdown函数如下:

void pushdown(int x){
    if(tag[x]){
        tag[x<<1]+=tag[x];
        tag[x<<1|1]+=tag[x];
        db t;

        t=SIN[x<<1];
        SIN[x<<1]=SIN[x<<1]*cos(tag[x])+COS[x<<1]*sin(tag[x]);
        COS[x<<1]=COS[x<<1]*cos(tag[x])-t*sin(tag[x]);

        t=SIN[x<<1|1];
        SIN[x<<1|1]=SIN[x<<1|1]*cos(tag[x])+COS[x<<1|1]*sin(tag[x]);
        COS[x<<1|1]=COS[x<<1|1]*cos(tag[x])-t*sin(tag[x]);
        tag[x]=0;
    }
}

其他部分就与普通线段树一样。

代码

#include<cstdio>
#include<cstdlib>
#include<cmath>
#define ll long long
#define db double
#define maxn (int)(2e5+10)
using namespace std;
ll a[maxn];
ll tag[maxn<<2];
db SIN[maxn<<2],COS[maxn<<2];
int l[maxn<<2],r[maxn<<2];
void update(int x){
    SIN[x]=SIN[x<<1]+SIN[x<<1|1];
    COS[x]=COS[x<<1]+COS[x<<1|1];
}
void pushdown(int x){
    if(tag[x]){
        tag[x<<1]+=tag[x];
        tag[x<<1|1]+=tag[x];
        db t;

        t=SIN[x<<1];
        SIN[x<<1]=SIN[x<<1]*cos(tag[x])+COS[x<<1]*sin(tag[x]);
        COS[x<<1]=COS[x<<1]*cos(tag[x])-t*sin(tag[x]);

        t=SIN[x<<1|1];
        SIN[x<<1|1]=SIN[x<<1|1]*cos(tag[x])+COS[x<<1|1]*sin(tag[x]);
        COS[x<<1|1]=COS[x<<1|1]*cos(tag[x])-t*sin(tag[x]);
        tag[x]=0;
    }
}
void build(int x,int left,int right){
    int mid;
    l[x]=left;r[x]=right;
    if(left==right){
        COS[x]=cos(a[left]);
        SIN[x]=sin(a[left]);
        return;
    }
    mid=left+right>>1;
    build(x<<1,left,mid);
    build(x<<1|1,mid+1,right);
    update(x);
    return;
}
void change(int x,int left,int right,ll key){
    if(l[x]>=left&&r[x]<=right){
        tag[x]+=key;
        db t=SIN[x];
        SIN[x]=SIN[x]*cos(key)+COS[x]*sin(key);
        COS[x]=COS[x]*cos(key)-t*sin(key);
        return;
    }
    pushdown(x);
    int mid=l[x]+r[x]>>1;
    if(left<=mid) change(x<<1,left,right,key);
    if(right>mid) change(x<<1|1,left,right,key);
    update(x);
    return;
}
db query(int x,int left,int right){
    db ans=0;
    if(l[x]>=left&&r[x]<=right) return SIN[x];
    pushdown(x);
    int mid=l[x]+r[x]>>1;
    if(left<=mid) ans+=query(x<<1,left,right);
    if(right>mid) ans+=query(x<<1|1,left,right);
    update(x);
    return ans;
}
int main(){
    int n,m,i,j,op,x,y,z;
    scanf("%d",&n);
    for(i=1;i<=n;++i) scanf("%lld",&a[i]);
    build(1,1,n);
    scanf("%d",&m);
    for(i=1;i<=m;++i){
        scanf("%d",&op);
        if(op==1){
            scanf("%d%d%d",&x,&y,&z);
            change(1,x,y,z);
        }
        if(op==2){
            scanf("%d%d",&x,&y);
            printf("%.1lf\n",query(1,x,y));
        }
    }
    // system("pause");
    return 0;
}
posted @ 2021-11-22 22:04  文艺平衡树  阅读(20)  评论(0编辑  收藏  举报