无聊的数列线段树
Posted qseer
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了无聊的数列线段树相关的知识,希望对你有一定的参考价值。
题目背景
无聊的YYB总喜欢搞出一些正常人无法搞出的东西。有一天,无聊的YYB想出了一道无聊的题:无聊的数列。。。(K峰:这题不是傻X题吗)
题目描述
维护一个数列{a[i]},支持两种操作:
1、1 L R K D:给出一个长度等于R-L+1的等差数列,首项为K,公差为D,并将它对应加到a[L]~a[R]的每一个数上。即:令a[L]=a[L]+K,a[L+1]=a[L+1]+K+D,
a[L+2]=a[L+2]+K+2D……a[R]=a[R]+K+(R-L)D。
2、2 P:询问序列的第P个数的值a[P]。
输入输出格式
输入格式:
第一行两个整数数n,m,表示数列长度和操作个数。
第二行n个整数,第i个数表示a[i](i=1,2,3…,n)。
接下来的m行,表示m个操作,有两种形式:
1 L R K D
2 P 字母意义见描述(L≤R)。
输出格式:
对于每个询问,输出答案,每个答案占一行。
输入输出样例
说明
数据规模:
0≤n,m≤100000
|a[i]|,|K|,|D|≤200
神奇的线段树,本来线段树的用法都是静态地操作,现在发现连这样的动态的等差数列都可以添加
其实关键是利用了差分的原理,通过线段树可以很好地发挥差分的优势
code
#include<stdio.h> #include<algorithm> #define ls x<<1 #define rs x<<1|1 using namespace std; const int mxn=100010; int n,m,K,D; int a[mxn],laz[mxn<<2],tree[mxn<<2]; inline int In() { int s=0,f=1; char ch=getchar(); while(ch>‘9‘ || ch<‘0‘) { if(ch==‘-‘) f=-1; ch=getchar(); } while(ch>=‘0‘ && ch<=‘9‘) s=s*10+ch-‘0‘,ch=getchar(); return s*f; } void pd(int x,int l,int r,int mid) { if(laz[x]) { laz[ls]+=laz[x]; laz[rs]+=laz[x]; tree[ls]+=(mid-l+1)*laz[x]; tree[rs]+=(r-mid)*laz[x]; laz[x]=0; } } void add(int x,int l,int r,int ql,int qr,int val) { if(ql<=l && qr>=r) { tree[x]+=val*(r-l+1),laz[x]+=val; return ; } int mid=(l+r)>>1; pd(x,l,r,mid); if(ql<=mid) add(ls,l,mid,ql,qr,val); if(qr>mid) add(rs,mid+1,r,ql,qr,val); tree[x]=tree[ls]+tree[rs]; } int qur(int x,int l,int r,int ql,int qr) { if(ql<=l && qr>=r) return tree[x]; int mid=(l+r)>>1,re=0; pd(x,l,r,mid); if(ql<=mid) re+=qur(ls,l,mid,ql,qr); if(qr>mid) re+=qur(rs,mid+1,r,ql,qr); return re; } int main() { scanf("%d%d",&n,&m); for(int i=1;i<=n;++i) scanf("%d",&a[i]); for(int i=1;i<=m;++i) { int cas; scanf("%d",&cas); if(cas==1) { int ql,qr; scanf("%d%d%d%d",&ql,&qr,&K,&D); add(1,1,n,ql,ql,K); if(qr>ql) add(1,1,n,ql+1,qr,D); if(qr!=n) add(1,1,n,qr+1,qr+1,-K-(qr-ql)*D); } else if(cas==2) { int x; scanf("%d",&x); printf("%d ",a[x]+qur(1,1,n,1,x)); } } return 0; }
以上是关于无聊的数列线段树的主要内容,如果未能解决你的问题,请参考以下文章
[LuoguP1438]无聊的数列(差分+线段树/树状数组)