思路:
区间修改首先想到的就是线段树,考虑怎么样维护一个懒标。
标记跟标记,值跟值的合并都可以直接合并,如何处理标记跟值的合并。
∑ 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)
可以按照以下公式拆开:
∑sin(a+v)=∑(sina)∗cosv+∑(cosa)∗sinv
对于每个节点维护s i n a i和c o s a i的和就可以了。
代码:
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; }