线段树-洛谷P1438 无聊的数列
https://www.luogu.org/problem/show?pid=1438
一开始吓死,给区间加上一个等差数列???
然后就发现我们只要维护题目里说的k,d两个之就好了;
比如我读入是1,x,y,k,d;
那么对于在x~y这个区间里面的小区间l~r
小区间的k,d值相应就是
k’=k+d*(l-x);
d’=d;
这个很显然把;
换句话说,x~y变成一个等差序列,那么对于i小区间l~r显然也是一个等差序列;
那么直接线段树好了;
比什么毒瘤数据题好多了;
然后洛谷数据有毒;
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<algorithm>
#include<cstring>
using namespace std;
struct tree{
int l,r,k,d;
}T[262144];
int a[100005];
int n,m,x,y,z,k,d;
void maketree(int l,int r,int num){
T[num].l=l; T[num].r=r;
if(l==r)return;
int mid=l+r>>1; num=num<<1;
maketree(l,mid ,num );
maketree(mid+1,r,num+1);
}
void init(int num){
if(x<=T[num].l&&T[num].r<=y){
T[num].k+=k+d*(T[num].l-x);
T[num].d+=d;
return;
}
num=num<<1;
if(T[num ].r>=x)init(num );
if(T[num+1].l<=y)init(num+1);
}
int outit(int num){
if(T[num].l==T[num].r)
return a[T[num].l]+T[num].k;
int ans=T[num].k+T[num].d*(x-T[num].l);
num=num<<1;
if(T[num].r>=x)return ans+outit(num);
else return ans+outit(num+1);
}
int main()
{
scanf("%d%d",&n,&m);
for(int i=1;i<=n;i++)scanf("%d",&a[i]);
maketree(1,n,1);
while(m--){
scanf("%d",&z);//这里一定要用一个新的变量
if(z==1){
scanf("%d%d%d%d",&x,&y,&k,&d);
init(1);
}else{
scanf("%d",&x);
printf("%d\n",outit(1));
}
}
}
洛谷题解还有一个非常妙的思路;
实际上可以维护一个差分数组,不需要进行复杂的计算
只需要写一个很普通的滋磁区间加法和区间求和的线段树就可以了
1操作可以转化为如下操作:
将a[L]在差分数组中的对应值d[L]加上K
将a[L+1]至a[R]在差分数组中的对应值加上D
将a[R+1]对应的d[R+1]减去K+D*(R-L),即减去数列的末项
2操作即在差分数组中求d[1]至d[P]的和