题目连接:点击打开链接
从前有一个贸易市场,在一位执政官到来之前都是非常繁荣的,自从他来了之后,发布了一系列奇怪的政令,导致贸易市场的衰落。
有 n nn 个商贩,从 0∼n−1 0 \sim n - 10∼n−1 编号,每个商贩的商品有一个价格 ai a_iai,有两种政令:
- l,r,c l, r, cl,r,c,对于 i∈[l,r],ai←ai+c i \in [l, r], a_i \leftarrow a_i + ci∈[l,r],ai←ai+c
- l,r,d l, r, dl,r,d,对于 i∈[l,r],ai←⌊ai/d⌋ i \in [l, r], a_i \leftarrow \lfloor {a_i}/{d} \rfloori∈[l,r],ai←⌊ai/d⌋
现在有一个外乡的旅客想要了解贸易市场的信息,有两种询问方式:
- 给定 l,r l, rl,r,求 mini∈[l,r]ai \min_{i \in [l, r]} a_imini∈[l,r]ai
- 给定 l,r l, rl,r,求 ∑i∈[l,r]ai \sum_{i\in [l, r]} a_i∑i∈[l,r]ai
Input
第一行为两个空格隔开的整数 n,q n, qn,q 分别表示商贩个数和政令 + 询问个数。
第二行包含 n nn 个由空格隔开的整数 a0∼an−1 a_0 \sim a_{n - 1}a0∼an−1
接下来 q qq 行,每行表示一个操作,第一个数表示操作编号 1∼4 1 \sim 41∼4,接下来的输入和问题描述一致。
Output
对于每个 3、4 操作,输出询问答案。
线段树的操作,有一点这题除法的时候是向下取整,当时没看见,卡了很久。
不知道为什么这种线段树总感觉怪怪的,不能真正诠释线段树这个东西,感觉在数据比较极端的情况下会超时。
哎,可能是因为我太菜了,没能理解。。。
首先这里有一个向下取整的操作,就很烦。那么我们把除法变成减法的操作就简单了,但必须是区间内所有的数减的是一样的。
那咋办,,判断最大的数需要减多少盒最小的数需要减多少,如果一样的话就用区间更新的操作,如果不一样那就继续搜。。
还有一点就是负数和非负数要分情况讨论,其他也就没什么了。。
#include<bits/stdc++.h> using namespace std; typedef long long ll; const int maxn=100010; const ll inf=1e18; ll sum[maxn<<2],lazy[maxn<<2]; ll d[maxn<<2],x[maxn<<2]; void up(int root) { sum[root]=sum[root<<1]+sum[root<<1|1]; d[root]=max(d[root<<1],d[root<<1|1]); x[root]=min(x[root<<1],x[root<<1|1]); return; } void down(int root,int l,int r) { lazy[root<<1]+=lazy[root]; lazy[root<<1|1]+=lazy[root]; int mid=(l+r)/2; sum[root<<1]+=(mid-l+1)*lazy[root]; sum[root<<1|1]+=(r-mid)*lazy[root]; x[root<<1]+=lazy[root]; x[root<<1|1]+=lazy[root]; d[root<<1]+=lazy[root]; d[root<<1|1]+=lazy[root]; lazy[root]=0; } void build(int root,int l,int r) { lazy[root]=0; if(l==r) { scanf("%lld",&sum[root]); d[root]=x[root]=sum[root]; return; } int mid=(l+r)/2; build(root<<1,l,mid); build(root<<1|1,mid+1,r); up(root); } void updata1(int root,int nowa,int nowb,int l,int r,int val) { if(nowb<l||r<nowa) return; if(l<=nowa&&nowb<=r) { sum[root]+=(nowb-nowa+1)*val; d[root]+=val; x[root]+=val; lazy[root]+=val; return; } if(lazy[root]) down(root,nowa,nowb); int mid=(nowa+nowb)/2; updata1(root<<1,nowa,mid,l,r,val); updata1(root<<1|1,mid+1,nowb,l,r,val); up(root); } void updata2(int root,int nowa,int nowb,int l,int r,int val) { if(nowb<l||r<nowa) return; if(l<=nowa&&nowb<=r) { ll X,Y; if (x[root]>=0) X=x[root]/val; else X=(x[root]-val+1)/val; if (d[root]>=0) Y=d[root]/val; else Y=(d[root]-val+1)/val; if (X-x[root]==Y-d[root]) { lazy[root]+=X-x[root]; sum[root]+=(X-x[root])*(nowb-nowa+1); d[root]+=X-x[root]; x[root]+=X-x[root]; return ; } } if(lazy[root]) down(root,nowa,nowb); int mid=(nowa+nowb)/2; updata2(root<<1,nowa,mid,l,r,val); updata2(root<<1|1,mid+1,nowb,l,r,val); up(root); } ll query1(int root,int nowa,int nowb,int l,int r) { if(nowb<l||r<nowa) return 0; if(l<=nowa&&nowb<=r) return sum[root]; if(lazy[root]) down(root,nowa,nowb); int mid=(nowa+nowb)/2; ll ans=0; ans+=query1(root<<1,nowa,mid,l,r); ans+=query1(root<<1|1,mid+1,nowb,l,r); up(root); return ans; } ll query2(int root,int nowa,int nowb,int l,int r) { if(nowb<l||r<nowa) return inf; if(l<=nowa&&nowb<=r) return x[root]; if(lazy[root]) down(root,nowa,nowb); int mid=(nowa+nowb)/2; ll ans=inf; ans=min(ans,query2(root<<1,nowa,mid,l,r)); ans=min(ans,query2(root<<1|1,mid+1,nowb,l,r)); up(root); return ans; } int main() { int n,q,op,x,y,z; scanf("%d%d",&n,&q); build(1,0,n-1); while(q--) { scanf("%d",&op); if(op==1) { scanf("%d%d%d",&x,&y,&z); updata1(1,0,n-1,x,y,z); } else if(op==2) { scanf("%d%d%d",&x,&y,&z); updata2(1,0,n-1,x,y,z); } else if(op==3) { scanf("%d%d",&x,&y); printf("%lld\n",query2(1,0,n-1,x,y)); } else { scanf("%d%d",&x,&y); printf("%lld\n",query1(1,0,n-1,x,y)); } } return 0; }