2020.01.19日常总结

P 1438        \color{green}{洛谷P1438 \ \ \ \ \ \ 无聊的数列}

\color{blue}{【题意】:}
维护一个数列 a {a} ,支持两种操作:

1、1 L R K D:给出一个长度等于 R L + 1 R-L+1 等差数列,首项为 K K ,公差为 D D ,并将它对应加到 a [ L ] a [ R ] a[L]-a[R] 的每一个数上。即:令 a [ L ] = a [ L ] + K a [ L + 1 ] = a [ L + 1 ] + K + D a [ L + 2 ] = a [ L + 2 ] + K + 2 × D a [ R ] = a [ R ] + K + ( R L ) × D a[L]=a[L]+K,a[L+1]=a[L+1]+K+D,a[L+2]=a[L+2]+K+2\times D……a[R]=a[R]+K+(R-L)\times D

2、2 P:询问序列的第 P P 个数的值 a [ P ] a[P]

\color{blue}{【思路】:} 我们把 a a 数组进行差分得到 c c ,我们考虑每一次1操作 c c 数组有什么影响。

我们可以发现,每一次的1操作会让c[l]+=f,c[l+1..r]+=d,f[r+1]+=-f-(r-l)*d f f 表示首项, d d 表示公差)。

举个例子:

下标:     1  2...l  l+1   l+2     l+3...     r           r+1      r+2...
a数组增量:+0 +0  +f +f+d +f+2*d  +f+3*d  +f+(r-l)*d       +0       +0
c数组增量:+0 +0  +f  +d    +d      +d        +d       -f-(r-l)*d   +0

从这个表格中我们可以看到一些规律:我们发现直接差分需要将区间 [ l + 1 , r ] [l+1,r] c c 都增加 d d ,所以我们可以联想到 线 \color{red}{线段树}

讲讲线段树的实现。其实大家对线段树的基本概念都有了解(当默认),重点讲讲标记下传。为了节省时间,我们不能每次都递归到叶子节点计算,所以我们为每一个区间打上一个标记,代表这个区间所有数都加上了这个数。当我们需要遍历到一个节点,准备访问其叶子节点的时候,我们就把标记下放。因为标记下放的时间通常可以不计,默认为 O ( 1 ) O(1) ,所以我们就在遍历节点的时候随便把标记下放了,没有增加时间复杂度又保证了正确性。

总的时间复杂度: O ( ( M + N ) × l o g   N ) O((M+N) \times log\ N) ,可以通过本题。

\color{blue}{【代码】:}

const int N=1e5+100;
#define ll long long
#define gc getchar()
#define g(c) isdigit(c)
inline ll read(){
	char c=0;ll x=0;bool f=0;
	while (!g(c)) f=c=='-',c=gc;
	while (g(c)) x=x*10+c-48,c=gc;
	return f?-x:x;
}//快读程序
ll sum[N<<2],add[N<<2];
inline void pushup(int o){
	sum[o]=sum[o<<1]+sum[o<<1|1];
}//计算当前节点所代表的区间和
inline void pushdown(int o,int l,int r){
	ll tag=add[o];add[o]=0ll;
	add[o<<1]+=tag;add[o<<1|1]+=tag;
	register int mid=(l+r)>>1;
	sum[o<<1]+=tag*(mid-l+1);
	sum[o<<1|1]+=tag*(r-mid);
}//标记下放,时间复杂度可认为是O(1)
void build(int o,int l,int r){
	sum[o]=add[o]=0ll;
	if (l==r) return;
	register int mid=(l+r)>>1;
	build(o<<1,l,mid);
	build(o<<1|1,mid+1,r);
	pushup(o);return;
}//基于本题的建树操作
void updata(int o,int l,int r,int p,int q,ll v){
	if (l>q||r<p) return;
	if (p<=l&&r<=q){
		sum[o]+=v*(r-l+1);
		add[o]+=v;return;
	}
	if (add[o]) pushdown(o,l,r);
	register int mid=(l+r)>>1;
	updata(o<<1,l,mid,p,q,v);
	updata(o<<1|1,mid+1,r,p,q,v);
	pushup(o);return;
}//区间修改:把区间[p,q]的每一个数都加上v
ll query(int o,int l,int r,int p,int q){
	if (l>q||r<p) return 0ll;
	if (p<=l&&r<=q) return sum[o];
	if (add[o]) pushdown(o,l,r);
	register int mid=(l+r)>>1;
	register ll ans=0ll;
	ans+=query(o<<1,l,mid,p,q);
	ans+=query(o<<1|1,mid+1,r,p,q);
	return ans;
}//询问操作:询问区间[p,q]的总和
int n,m;ll a[N];
int main(){
	n=read();m=read();
	for(int i=1;i<=n;i++)
		a[i]=read();
	build(1,1,n);
	for(int i=1;i<=m;i++){
		ll opt=read(),l=read();
		if (opt==1){
			ll r=read(),f=read(),d=read();
			if (l==r){
				updata(1,1,n,l,l,f);
				if (l!=n) updata(1,1,n,l+1,l+1,-f);
			}
			else{
				updata(1,1,n,l,l,f);updata(1,1,n,l+1,r,d);
				if (r!=n) updata(1,1,n,r+1,r+1,-f-(r-l)*d);
			}
		}
		else printf("%lld\n",query(1,1,n,1,l)+a[l]);
	}
	return 0;
}
发布了82 篇原创文章 · 获赞 4 · 访问量 1761

猜你喜欢

转载自blog.csdn.net/ZHUYINGYE_123456/article/details/104045062