雅礼集训2017day1市场

雅礼集训2017day1市场

题目大意

给你一个长度为 n n n的序列 a a a,有 q q q次操作,操作有四种:

  • 1,l,r,c:对于 i ∈ [ l , r ] i\in[l,r] i[l,r] a i = a i + c a_i=a_i+c ai=ai+c
  • 2,l,r,d:对于 i ∈ [ l , r ] i\in[l,r] i[l,r] a i = ⌊ a i d ⌋ a_i=\lfloor\dfrac{a_i}{d}\rfloor ai=dai
  • 3,l,r:求 min ⁡ i = l r a i \min\limits_{i=l}^ra_i i=lminrai
  • 4,l,r:求 ∑ i = l r a i \sum\limits_{i=l}^ra_i i=lrai

题解

这道题涉及到区间修改和区间查询,我们可以用线段树来维护序列 a a a

对于操作1,直接区间修改即可。对于操作3和操作4,直接区间查询即可。

对于操作2,我们需要把区间除法转化为区间减法。对每一个区间维护一个最小值 m n mn mn和最大值 m x mx mx,如果 m n − ⌊ m n d ⌋ = m x − ⌊ m x d ⌋ mn-\lfloor\frac{mn}{d}\rfloor=mx-\lfloor\frac{mx}{d}\rfloor mndmn=mxdmx,则说明这一段中每个数减去这个数除以 d d d后的值的差都相等,那么将区间中的每一个数都减去 m n − ⌊ m n d ⌋ mn-\lfloor\frac{mn}{d}\rfloor mndmn后就能得到这个数除以 d d d后的值。

那为什么这样做不会TLE呢?

首先,如果只有除法,则对于 a i a_i ai,最多经过 log ⁡ a i \log a_i logai次除法就会变成 1 1 1 0 0 0,然后就可以进行上面的操作了,时间复杂度可记为 O ( log ⁡ C ) O(\log C) O(logC),其中 C = 1 0 9 + 1 0 4 ⋅ q C=10^9+10^4\cdot q C=109+104q,即 a i a_i ai可能达到的最大值。

但因为还有加法操作,每次操作最多会使一个可以将区间除法用区间减法来做的区间分成 log ⁡ n \log n logn块,这些块都将要经过 log ⁡ C \log C logC次除法才能合并,也就是将要进行 O ( log ⁡ n log ⁡ C ) O(\log n\log C) O(lognlogC)的时间复杂度的除法,则 q q q次操作的时间复杂度为 O ( q log ⁡ n log ⁡ C ) O(q\log n\log C) O(qlognlogC)。一开始最多有 n n n个这样的区间,最多会导致的除法分时间复杂度为 O ( n log ⁡ C ) O(n\log C) O(nlogC)。直接的区间修改和区间查询的时间复杂度为 O ( n log ⁡ n ) O(n\log n) O(nlogn)。所以总时间复杂度为 O ( n log ⁡ C + q log ⁡ n log ⁡ C ) O(n\log C+q\log n\log C) O(nlogC+qlognlogC)

code

#include<bits/stdc++.h>
#define lc k<<1
#define rc k<<1|1
using namespace std;
int n,q,a[500005];
long long ans,v[500005],mn[500005],mx[500005],ly[500005];
void pt(int k){
	v[k]=v[lc]+v[rc];
	mn[k]=min(mn[lc],mn[rc]);
	mx[k]=max(mx[lc],mx[rc]);
}
void build(int k,int l,int r){
	if(l==r){
		v[k]=mn[k]=mx[k]=a[l];
		return;
	}
	int mid=l+r>>1;
	build(lc,l,mid);
	build(rc,mid+1,r);
	pt(k);
}
void add(int k,int len,int c){
	v[k]+=len*c;
	mn[k]+=c;
	mx[k]+=c;
	ly[k]+=c;
}
void down(int k,int l,int r){
	int mid=l+r>>1;
	add(lc,mid-l+1,ly[k]);
	add(rc,r-mid,ly[k]);
	ly[k]=0;
}
void ch(int k,int l,int r,int x,int y,int c){
	if(l>=x&&r<=y){
		add(k,r-l+1,c);
		return;
	}
	if(ly[k]) down(k,l,r);
	int mid=l+r>>1;
	if(x<=mid) ch(lc,l,mid,x,y,c);
	if(y>mid) ch(rc,mid+1,r,x,y,c);
	pt(k);
}
void dv(int k,int l,int r,int x,int y,int c){
	if(l>=x&&r<=y){
		int v1=floor((double)mn[k]/c),v2=floor((double)mx[k]/c);
		if(mn[k]-v1==mx[k]-v2){
			add(k,r-l+1,-mn[k]+v1);
			return;
		}
	}
	if(ly[k]) down(k,l,r);
	int mid=l+r>>1;
	if(x<=mid) dv(lc,l,mid,x,y,c);
	if(y>mid) dv(rc,mid+1,r,x,y,c);
	pt(k);
}
void gtmin(int k,int l,int r,int x,int y){
	if(l>=x&&r<=y){
		ans=min(ans,mn[k]);
		return;
	}
	if(ly[k]) down(k,l,r);
	int mid=l+r>>1;
	if(x<=mid) gtmin(lc,l,mid,x,y);
	if(y>mid) gtmin(rc,mid+1,r,x,y);
}
void gtsum(int k,int l,int r,int x,int y){
	if(l>=x&&r<=y){
		ans+=v[k];
		return;
	}
	if(ly[k]) down(k,l,r);
	int mid=l+r>>1;
	if(x<=mid) gtsum(lc,l,mid,x,y);
	if(y>mid) gtsum(rc,mid+1,r,x,y);
}
int main()
{
	scanf("%d%d",&n,&q);
	for(int i=1;i<=n;i++){
		scanf("%d",&a[i]);
	}
	build(1,1,n);
	int tp,l,r,c;
	while(q--){
		scanf("%d",&tp);
		if(tp==1){
			scanf("%d%d%d",&l,&r,&c);++l;++r;
			ch(1,1,n,l,r,c);
		}
		else if(tp==2){
			scanf("%d%d%d",&l,&r,&c);
			++l;++r;
			dv(1,1,n,l,r,c);
		}
		else if(tp==3){
			scanf("%d%d",&l,&r);++l;++r;
			ans=1e15;
			gtmin(1,1,n,l,r);
			printf("%lld\n",ans);
		}
		else{
			scanf("%d%d",&l,&r);++l;++r;
			ans=0;
			gtsum(1,1,n,l,r);
			printf("%lld\n",ans);
		}
	}
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值