\[ \texttt{Preface} \]
第二道 Ynoi 的题,纪念一下。
这可能是我唯一可以自己做的 Ynoi 题了。
\[ \texttt{Description} \]
维护一个长度为 \(n\) 的数列 \(a\),支持两种操作:
1 l r v
将 \(a_l,a_{l+1},...,a_r\) 分别加上 \(v\)2 l r
询问 \(\sum\limits_{i=l}\limits^{r}\sin(a_i)\) 。
\[ \texttt{Solution} \]
区间修改和区间查询使我们想到线段树。
尝试在线段树上维护一个区间 \(\sin\) 和。
我们发现,区间加的时候,原来的 \(\sin(a_i)\) 都变成了 \(\sin(a_i+v)\) ,不易直接维护。
考虑这两个公式:
\[ \sin(a+v)=\sin a\cos v+\cos a\sin v \]\[ \cos(a+v)=\cos a\cos v-\sin a \sin v \]
显然区间加将区间 \([l,r]\) 的 \(\sin\) 和从 \(\sum\limits_{i=l}\limits^{r}\sin(a_i)\) 变成了 \(\sum\limits_{i=l}\limits^{r}\sin(a_i+v)\) ,进一步,有:
\[ \sum\limits_{i=l}\limits^{r}\sin a_i \cos v+ \cos a_i \sin v \]
\[ \cos v \sum\limits_{i=l}\limits^{r}\sin a_i + \sin v\sum\limits_{i=l}\limits^{r}\cos a_i \]
- 于是我们可以再维护一个区间 \(\cos\) 和, 区间加对 \([l,r]\) 的 \(\cos\) 和的影响为:
\[ \cos v \sum\limits_{i=l}\limits^{r} \cos a_i -\sin v\sum\limits_{i=l}\limits^{r} \sin a_i \]
- 套用上述公式更新区间 \(\sin\) 和以及区间 \(\cos\) 和即可,记得打好标记。
\[ \texttt{Code} \]
#include
#include
#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
\[ \texttt{Thanks} \ \texttt{for} \ \texttt{watching} \]