洛谷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;
}