题解【[Ynoi2012]NOIP2015洋溢着希望】

\[ \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<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\n",ask(1,l,r));

                break;
            }
        }
    }

    return 0;
}

\[ \texttt{Thanks} \ \texttt{for} \ \texttt{watching} \]

猜你喜欢

转载自www.cnblogs.com/cjtcalc/p/12324869.html