[ exttt{Preface}
]
第二道 Ynoi 的题,纪念一下。
这可能是我唯一可以自己做的 Ynoi 题了。
[ exttt{Description}
]
维护一个长度为 (n) 的数列 (a),支持两种操作:
1 l r v
将 (a_l,a_{l+1},...,a_r) 分别加上 (v)2 l r
询问 (sumlimits_{i=l}limits^{r}sin(a_i)) 。
[ exttt{Solution}
]
-
区间修改和区间查询使我们想到线段树。
-
尝试在线段树上维护一个区间 (sin) 和。
-
我们发现,区间加的时候,原来的 (sin(a_i)) 都变成了 (sin(a_i+v)) ,不易直接维护。
-
考虑这两个公式:
[sin(a+v)=sin acos v+cos asin v ][cos(a+v)=cos acos v-sin a sin v ] -
显然区间加将区间 ([l,r]) 的 (sin) 和从 (sumlimits_{i=l}limits^{r}sin(a_i)) 变成了 (sumlimits_{i=l}limits^{r}sin(a_i+v)) ,进一步,有:
[sumlimits_{i=l}limits^{r}sin a_i cos v+ cos a_i sin v ]
[cos v sumlimits_{i=l}limits^{r}sin a_i + sin vsumlimits_{i=l}limits^{r}cos a_i
]
- 于是我们可以再维护一个区间 (cos) 和, 区间加对 ([l,r]) 的 (cos) 和的影响为:
[cos v sumlimits_{i=l}limits^{r} cos a_i -sin vsumlimits_{i=l}limits^{r} sin a_i
]
- 套用上述公式更新区间 (sin) 和以及区间 (cos) 和即可,记得打好标记。
[ exttt{Code}
]
#include<cstdio>
#include<cmath>
#define RI register int
using namespace std;
namespace IO
{
static char buf[1<<20],*fs,*ft;
inline char gc()
{
if(fs==ft)
{
ft=(fs=buf)+fread(buf,1,1<<20,stdin);
if(fs==ft)return EOF;
}
return *fs++;
}
#define gc() getchar()
inline int read()
{
int x=0,f=1;char s=gc();
while(s<'0'||s>'9'){if(s=='-')f=-f;s=gc();}
while(s>='0'&&s<='9'){x=x*10+s-'0';s=gc();}
return x*f;
}
}using IO::read;
const int N=200100;
int n,m;
int a[N];
struct SegmentTree{
int l,r;
double sinv,cosv;
long long tag;
}t[N*4];
void upd(int p)
{
t[p].sinv=t[p*2].sinv+t[p*2+1].sinv;
t[p].cosv=t[p*2].cosv+t[p*2+1].cosv;
}
void spread(int p)
{
if(t[p].tag)
{
double sina,cosa,sinx=sin(t[p].tag),cosx=cos(t[p].tag);
sina=t[p*2].sinv,cosa=t[p*2].cosv;
t[p*2].sinv=sina*cosx+cosa*sinx;
t[p*2].cosv=cosa*cosx-sina*sinx;
sina=t[p*2+1].sinv,cosa=t[p*2+1].cosv;
t[p*2+1].sinv=sina*cosx+cosa*sinx;
t[p*2+1].cosv=cosa*cosx-sina*sinx;
t[p*2].tag+=t[p].tag;
t[p*2+1].tag+=t[p].tag;
t[p].tag=0;
}
}
void build(int p,int l,int r)
{
t[p].l=l,t[p].r=r;
if(l==r)
{
t[p].sinv=sin(a[l]),t[p].cosv=cos(a[l]);
return;
}
int mid=(l+r)/2;
build(p*2,l,mid);
build(p*2+1,mid+1,r);
upd(p);
}
void change(int p,int l,int r,int val)
{
if(l<=t[p].l&&t[p].r<=r)
{
double sina=t[p].sinv,cosa=t[p].cosv,sinx=sin(val),cosx=cos(val);
t[p].sinv=sina*cosx+cosa*sinx;
t[p].cosv=cosa*cosx-sina*sinx;
t[p].tag+=val;
return;
}
spread(p);
int mid=(t[p].l+t[p].r)/2;
if(l<=mid)
change(p*2,l,r,val);
if(mid<r)
change(p*2+1,l,r,val);
upd(p);
}
double ask(int p,int l,int r)
{
if(l<=t[p].l&&t[p].r<=r)return t[p].sinv;
spread(p);
int mid=(t[p].l+t[p].r)/2;
double val=0;
if(l<=mid)
val+=ask(p*2,l,r);
if(mid<r)
val+=ask(p*2+1,l,r);
return val;
}
int main()
{
n=read();
for(RI i=1;i<=n;i++)
a[i]=read();
build(1,1,n);
m=read();
while(m--)
{
int opt=read(),l=read(),r=read();
switch(opt)
{
case 1:{
double val; scanf("%lf",&val);
change(1,l,r,val);
break;
}
case 2:{
printf("%.1lf
",ask(1,l,r));
break;
}
}
}
return 0;
}
[ exttt{Thanks} exttt{for} exttt{watching}
]