无聊的数列[题解]
无聊的数列
题目大意
给定一个数列 \(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了一个点,已改
本文来自博客园,作者:whrwlx,转载请注明原文链接:https://www.cnblogs.com/whrwlx/p/18062779