无聊的数列
题目大意
给定一个数列 \(A\)
有两种操作:
- 将数列中 \(A_i\) (\(L \leq i \leq R\)) 加上一个等差数列(首项D 公差K)
- 查询数列中第P位数
区间加上一个等差数列可以用差分来解决
例
原序列:0 0 0 0 0 0
差分序列:0 0 0 0 0 0
等差序列:1 3 5 7 9(首项1 公差2 L=1 R=5)
加上等差数列后的序列:1 3 5 7 9 0
然后差分:1 2 2 2 2 -9
我们可以发现,后来的差分序列中:
对于[L],加上了首项1
对于[L+1,R],每项都加上了公差2
对于[R+1],减去了末项9
所以该题是一个差分数组上区间修改,单点查询的题 即线段树
我们可以用线段树来维护差分数组
对于每个加上等差数列
我们可以:
[L]加上了首项
[L+1,R]加上公差
[R+1]减去末项
注:
L和R可能大于n!!!
所以要特判!!!
坑啊
所以代码就是
#include<bits/stdc++.h>
#define put(n) scanf("%lld",&n)
#define out(n) printf("%lld\n",n)
#define ll long long
using namespace std;
ll n,m,a[5001000];
struct ST
{
ll l,r;
ll add,ans;//懒标记 答案
}st[20001000];
void upd(ll p)
{
st[p].ans=(st[p<<1].ans+st[p<<1|1].ans);
}
void spread(ll p) //参考 区间加 代码
{
st[p<<1].ans+=st[p].add*(st[p<<1].r-st[p<<1].l+1);
st[p<<1|1].ans+=st[p].add*(st[p<<1|1].r-st[p<<1|1].l+1);
//两儿子 总和加上其长度*add
st[p<<1].add+=st[p].add;
st[p<<1|1].add+=st[p].add;
//下传
st[p].add=0;
//清空
}
void build(ll p,ll l,ll r)
{
st[p].l=l;
st[p].r=r;
st[p].add=st[p].ans=0;
if(l==r)
{
st[p].ans=a[l]-a[l-1];//差分
return;
}
ll mid=(l+r)>>1;
build(p<<1,l,mid);
build(p<<1|1,mid+1,r);
upd(p);
}
ll ask(ll p,ll l,ll r)//求区间和代码
{
if(l<=st[p].l&&r>=st[p].r)
return st[p].ans;
spread(p);
ll mid=(st[p].l+st[p].r)>>1;
ll sum=0;
if(l<=mid) sum+=ask(p<<1,l,r);
if(r>mid) sum+=ask(p<<1|1,l,r);
return sum;
}
void change(ll p,ll l,ll r,ll q)
{
if(l<=st[p].l&&r>=st[p].r)
{
st[p].ans+=q*(st[p].r-st[p].l+1);//总和加上其长度*add
st[p].add=st[p].add+q;//更新add
return;
}
spread(p);
ll mid=(st[p].l+st[p].r)>>1;
if(l<=mid) change(p<<1,l,r,q);
if(r>mid) change(p<<1|1,l,r,q);
upd(p);
}
int main()
{
freopen("boring.in","r",stdin);
freopen("boring.out","w",stdout);
scanf("%lld",&n);
scanf("%lld",&m);
for(ll i=1;i<=n;i++)
{
scanf("%lld",&a[i]);
}
build(1,1,n);
for(ll i=1;i<=m;i++)
{
ll opt,dd,le,ri,kk;
scanf("%lld",&opt);
if(opt==1)
{
scanf("%lld%lld%lld%lld",&le,&ri,&kk,&dd);
if(le<n)//坑
{
change(1ll,le,le,kk);//把L加上首项
if(ri<=n) change(1ll,le+1,ri,dd);//L+1 —R全加上公差
else change(1ll,le+1,n,dd);//坑
}
if(ri<n) change(1ll,ri+1,ri+1,-(dd*(ri-le)+kk));//R+1减去末项
}
else
{
scanf("%lld",&kk);
printf("%lld\n",ask(1ll,1,kk));
/*
查询差分数组前缀和
而不是ask(1,kk,kk)+a[kk]
*/
}
}
return 0;
}
标签:数列,无聊,题解,ll,差分,st,加上,add
From: https://www.cnblogs.com/whrwlx/p/18062779