無聊的數列[題解]

whrwlx發表於2024-03-09

無聊的數列


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

相關文章