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

[ 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} ]

原文地址:https://www.cnblogs.com/cjtcalc/p/12324869.html