無聊的數列
[link1] [link2]
題目大意
給定一個數列 \(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;
}