Ynoi难得的很好写的题。主要考察两角和公式:
$\sin(\alpha+\beta)=\sin\alpha\cos\beta+\cos\alpha\sin\beta$
$\cos(\alpha+\beta)=\cos\alpha\cos\beta-\sin\alpha\sin\beta$
接下来就是线段树模板了:
#include#include #define For(i,A,B) for(i=(A);i<=(B);++i) typedef long long ll; const int N=200050; const int BUF=1<<21; char rB[BUF],*rS,*rT; inline char gc(){return rS==rT&&(rT=(rS=rB)+fread(rB,1,BUF,stdin),rS==rT)?EOF:*rS++;} inline int rd(){ char c=gc(); while(c<48||c>57)c=gc(); int x=c&15; for(c=gc();c>=48&&c<=57;c=gc())x=(x<<3)+(x<<1)+(c&15); return x; } int a[N],x,y,k; double ssin[N<<2],scos[N<<2]; ll addv[N<<2]; inline void upd(int o,ll x){ double sinx=sin(x),cosx=cos(x),t=ssin[o]*cosx+scos[o]*sinx; scos[o]=scos[o]*cosx-ssin[o]*sinx; ssin[o]=t; addv[o]+=x; } inline void pushup(int o){ int lc=o<<1,rc=lc|1; ssin[o]=ssin[lc]+ssin[rc];scos[o]=scos[lc]+scos[rc]; } inline void pushdown(int o){ int lc=o<<1,rc=lc|1; upd(lc,addv[o]);upd(rc,addv[o]); addv[o]=0ll; } void build(int o,int L,int R){ if(L==R){ssin[o]=sin(a[L]);scos[o]=cos(a[L]);} else{ int lc=o<<1,rc=lc|1,M=L+R>>1; build(lc,L,M);build(rc,M+1,R); pushup(o); } } void add(int o,int L,int R){ if(x<=L&&y>=R)upd(o,k); else{ int lc=o<<1,rc=lc|1,M=L+R>>1; if(addv[o])pushdown(o); if(x<=M)add(lc,L,M); if(y>M)add(rc,M+1,R); pushup(o); } } double query(int o,int L,int R){ if(x<=L&&y>=R)return ssin[o]; int lc=o<<1,rc=lc|1,M=L+R>>1; double ans=0; if(addv[o])pushdown(o); if(x<=M)ans=query(lc,L,M); if(y>M)ans+=query(rc,M+1,R); return ans; } int main(){ int n=rd(),q,i,opt; For(i,1,n)a[i]=rd(); build(1,1,n); q=rd(); while(q--){ opt=rd();x=rd();y=rd(); if(opt==1){ k=rd(); add(1,1,n); }else printf("%.1lf\n",query(1,1,n)); } return 0; }