题目描述
给出一个长度为n的整数序列a1,a2,...,an,进行m次操作,操作分为两类。
操作1:给出l,r,v,将al,al+1,...,ar分别加上v;
操作2:给出l,r,询问
输入描述:
第一行一个整数n
接下来一行n个整数表示a1,a2,...,an
接下来一行一个整数m
接下来m行,每行表示一个操作,操作1表示为1 l r v,操作2表示为2 l r
保证1≤n,m,ai,v≤200000;1≤l≤r≤n,v是整数
输出描述:
对每个操作2,输出一行,表示答案,四舍五入保留一位小数
保证答案的绝对值大于0.1,且答案的准确值的小数点后第二位不是4或5
数据随机生成(n,m人工指定,其余整数在数据范围内均匀选取),并去除不满足条件的操作2
示例1
输入
4
1 2 3 4
5
2 2 4
1 1 3 1
2 2 4
1 2 4 2
2 1 3
输出
0.3
-1.4
-0.3
解题思路:这个题一看就是需要线段树的维护,但是这个求sin有点难操作。我们可以把每一个位置的数拆分成cos(a[i])+isin(a[i])
那么当我们需要进行操作1的时候,我们只需要将那个数也拆成这样进行乘法运算就行,然后我们最后查询的时候我们只需要查区间的和的虚部就行。
Ps: real() 这个是实根
#include<stdio.h>
#include<string.h>
#include<algorithm>
#include<complex>
using namespace std;
const int maxn=2e5+10;
#define mem(a,b) memset(a,b,sizeof(a))
typedef complex<double> E;
int n,m,a[maxn];
E sum[maxn<<2],lz[maxn<<2];
void pushup(int k){
sum[k]=sum[k<<1]+sum[k<<1|1];
}
void pushdown(int k){
sum[k<<1]*=lz[k];lz[k<<1]*=lz[k];
sum[k<<1|1]*=lz[k];lz[k<<1|1]*=lz[k];
lz[k]=E(1,0);
}
void build(int l,int r,int k){
lz[k]=E(1,0);
if(l==r){
sum[k]=E(cos(a[l]),sin(a[l]));
return ;
}
int mid=(l+r)>>1;
build(l,mid,k<<1);build(mid+1,r,k<<1|1);
pushup(k);
}
void cha(int l,int r,int ql,int qr,E x,int k){
if(l==ql&&r==qr){
sum[k]*=x;
lz[k]*=x;
return ;
}
int mid=(l+r)>>1;
pushdown(k);
if(qr<=mid) cha(l,mid,ql,qr,x,k<<1);
else if(ql>mid) cha(mid+1,r,ql,qr,x,k<<1|1);
else cha(l,mid,ql,mid,x,k<<1),cha(mid+1,r,mid+1,qr,x,k<<1|1);
pushup(k);
}
double query(int l,int r,int ql,int qr,int k){
if(l==ql&&r==qr){
return sum[k].imag();
}
int mid=(l+r)>>1;
pushdown(k);
if(qr<=mid) return query(l,mid,ql,qr,k<<1);
else if(ql>mid) return query(mid+1,r,ql,qr,k<<1|1);
else return query(l,mid,ql,mid,k<<1)+query(mid+1,r,mid+1,qr,k<<1|1);
}
int main(){
int i,j;
scanf("%d",&n);
for(i=1;i<=n;i++) scanf("%d",&a[i]);
build(1,n,1);
scanf("%d",&m);
while(m--){
int ki,u,v,w;
scanf("%d",&ki);
if(ki==1){
scanf("%d%d%d",&u,&v,&w);
cha(1,n,u,v,E(cos(w),sin(w)),1);
}
else{
scanf("%d%d",&u,&v);
printf("%.1lf\n",query(1,n,u,v,1));
}
}
return 0;
}