无聊的数列[题解]

无聊的数列


[link1] [link2] [更好的阅读体验]


题目大意

给定一个数列 \(A\)

有两种操作:

  • 将数列中 \(A_i\) (\(L \leq i \leq R\)) 加上一个等差数列(首项D 公差K)

  • 查询数列中第P位数

数据范围

对于 \(30\%\) 数据,\(0\le n,m \le 10,|a_i|,|K|,|D|\le 10\)

对于 \(100\%\) 数据,\(0\le n,m \le 10^5,|a_i|,|K|,|D|\le 200\)


区间加上一个等差数列可以用差分来解决

原序列: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

所以该题是一个差分数组上区间修改,单点查询的题 即线段树


暴力加等差数列30pts不讲了(\(n,m \leq 10\))

我们可以用线段树来维护差分数组
对于每个加上等差数列
我们可以:

[L]加上首项

[L+1,R]加上公差

[R+1]减去末项

另;对于查询第P位数,其答案为1到P的差分数组前缀和

注:

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(le==n) change(1ll,le,le,kk);
			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;
}

update:少判断L=n的情况,洛谷WA了一个点,已改

posted @ 2024-03-09 15:34  whrwlx  阅读(16)  评论(0编辑  收藏  举报