原题链接
思路:
区间修改首先想到的就是线段树,考虑怎么样维护一个懒标。
标记跟标记,值跟值的合并都可以直接合并,如何处理标记跟值的合并。
∑
s
i
n
(
a
i
)
−
>
∑
s
i
n
(
a
i
+
v
)
\sum sin(a_i)->\sum sin(a_i+v)
∑sin(ai)−>∑sin(ai+v)
可以按照以下公式拆开:
∑
s
i
n
(
a
+
v
)
=
∑
(
s
i
n
a
)
∗
c
o
s
v
+
∑
(
c
o
s
a
)
∗
s
i
n
v
\sum sin(a+v)=\sum(sina)*cosv+\sum (cosa)*sinv
∑sin(a+v)=∑(sina)∗cosv+∑(cosa)∗sinv
对于每个节点维护
s
i
n
a
i
sina_i
sinai和
c
o
s
a
i
cosa_i
cosai的和就可以了。
代码:
const int maxn=2e5+7,inf=0x3f3f3f3f;
const double eps=1e-5;
struct node{
int l,r;
ll laz;
double sum_sin,sum_cos;
}tr[maxn*4];
int n,m,a[maxn];
double sinv,cosv;
void pushup(int u){
tr[u].sum_sin=tr[u<<1].sum_sin+tr[u<<1|1].sum_sin;
tr[u].sum_cos=tr[u<<1].sum_cos+tr[u<<1|1].sum_cos;
}
void add(int u,double sinx,double cosx){
double siny=tr[u].sum_sin,cosy=tr[u].sum_cos;
tr[u].sum_sin=siny*cosx+cosy*sinx;
tr[u].sum_cos=cosx*cosy-sinx*siny;
}
void pushdown(int u){
if(tr[u].laz){
double sinx=sin(tr[u].laz),cosx=cos(tr[u].laz);
add(u<<1,sinx,cosx);
add(u<<1|1,sinx,cosx);
tr[u<<1].laz+=tr[u].laz;
tr[u<<1|1].laz+=tr[u].laz;
tr[u].laz=0;
}
}
void build(int u,int l,int r){
tr[u].l=l,tr[u].r=r;
if(l==r){
tr[u].laz=0;
tr[u].sum_sin=sin(a[l]);
tr[u].sum_cos=cos(a[l]);
return ;
}
int mid=(l+r)/2;
build(u<<1,l,mid);build(u<<1|1,mid+1,r);
pushup(u);
}
void update(int u,int l,int r,int ql,int qr,int v){
if(l>=ql&&r<=qr){
add(u,sinv,cosv);
tr[u].laz+=v;
return ;
}
int mid=(l+r)/2;
pushdown(u);
if(ql<=mid) update(u<<1,l,mid,ql,qr,v);
if(qr>mid) update(u<<1|1,mid+1,r,ql,qr,v);
pushup(u);
}
double query(int u,int l,int r,int ql,int qr){
if(l>=ql&&r<=qr){
return tr[u].sum_sin;
}
pushdown(u);
double res=0;
int mid=(l+r)/2;
if(ql<=mid) res+=query(u<<1,l,mid,ql,qr);
if(qr>mid) res+=query(u<<1|1,mid+1,r,ql,qr);
return res;
}
int main(){
n=read;
rep(i,1,n) a[i]=read;
build(1,1,n);
m=read;
while(m--){
int op=read,l=read,r=read;
if(op==1){
int v=read;
sinv=sin(v),cosv=cos(v);
update(1,1,n,l,r,v);
}
else printf("%.1f\n",query(1,1,n,l,r));
}
return 0;
}