【题目】
题目描述:
输入格式:
第一行为两个空格隔开的整数 n,qn,qn,q 分别表示商贩个数和政令和询问个数。
第二行包含 nnn 个由空格隔开的整数 a0a_0a0 ∼ an−1a_{n−1}an−1
接下来 qqq 行,每行表示一个操作,第一个数表示操作编号 111 ∼ 444 ,接下来的输入和问题描述一致。
输出格式:
对于每个 333、444 操作,输出询问答案。
样例数据:
输入
10 10
-5 -4 -3 -2 -1 0 1 2 3 4
1 0 4 1
1 5 9 1
2 0 9 3
3 0 9
4 0 9
3 0 1
4 2 3
3 4 5
4 6 7
3 8 9
输出
-2
-2
-2
-2
0
1
1
提示:
【数据范围与提示】
对于 30%30\%30% 的数据,n,q≤103n,q≤10^3n,q≤103;
对于 60%60\%60% 的数据,保证数据随机;
对于 100%100\%100% 的数据,1≤n,q≤105,0≤l≤r≤n−1,c∈[−104,104],d∈[2,109]1≤n,q≤10^5,0≤l≤r≤n−1,c∈[−10^4,10^4],d∈[2,10^9]1≤n,q≤105,0≤l≤r≤n−1,c∈[−104,104],d∈[2,109]
【分析】
区间整除和区间开根号有点像。
由于每次区间整除之后区间的数都至少要减少到原来的一半,所以 logai\log a_ilogai 次之后区间的数就变为 111 了。
就算加上区间加操作,操作的次数也不会多太多。
我们维护一个区间 maxmaxmax 和 minminmin,假设某一次修改操作要除的数是 kkk,当 maxk=mink\frac{max} k=\frac{min} kkmax=kmin 时,就相当于区间减去 max−maxkmax-\frac{max}kmax−kmax。
如果不是的话就递归修改。
注意有负数,整除是向 000 取整,向下取整的话可以用 >>1>>1>>1 或 cmath 库里的函数 floor\mathrm{floor}floor。
【代码】
#include<cmath>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define N 100005
#define ll long long
using namespace std;
ll a[N],Max[N<<2],Min[N<<2],add[N<<2],sum[N<<2];
ll Div(ll x,ll y){return floor((double)x/y);}
void pushup(int root)
{
sum[root]=sum[root<<1]+sum[root<<1|1];
Max[root]=max(Max[root<<1],Max[root<<1|1]);
Min[root]=min(Min[root<<1],Min[root<<1|1]);
}
void addit(int root,int l,int r,int val)
{
add[root]+=val;
Min[root]+=val;
Max[root]+=val;
sum[root]+=1ll*(r-l+1)*val;
}
void pushdown(int root,int l,int r,int mid)
{
if(!add[root]) return;
addit(root<<1,l,mid,add[root]);
addit(root<<1|1,mid+1,r,add[root]);
add[root]=0;
}
void build(int root,int l,int r)
{
if(l==r)
{
Max[root]=Min[root]=sum[root]=a[l];
return;
}
int mid=(l+r)>>1;
build(root<<1,l,mid);
build(root<<1|1,mid+1,r);
pushup(root);
}
void Add(int root,int l,int r,int x,int y,int val)
{
if(l>=x&&r<=y)
{
addit(root,l,r,val);
return;
}
int mid=(l+r)>>1;
pushdown(root,l,r,mid);
if(x<=mid) Add(root<<1,l,mid,x,y,val);
if(y>mid) Add(root<<1|1,mid+1,r,x,y,val);
pushup(root);
}
void modify(int root,int l,int r,int x,int y,int val)
{
if(l>=x&&r<=y)
{
ll maxn=Div(Max[root],val),minn=Div(Min[root],val);
if(Max[root]-maxn==Min[root]-minn) {addit(root,l,r,maxn-Max[root]);return;}
}
int mid=(l+r)>>1;
pushdown(root,l,r,mid);
if(x<=mid) modify(root<<1,l,mid,x,y,val);
if(y>mid) modify(root<<1|1,mid+1,r,x,y,val);
pushup(root);
}
ll ask(int root,int l,int r,int x,int y)
{
if(l>=x&&r<=y)
return Min[root];
int mid=(l+r)>>1;
pushdown(root,l,r,mid);
if(y<=mid) return ask(root<<1,l,mid,x,y);
if(x>mid) return ask(root<<1|1,mid+1,r,x,y);
return min(ask(root<<1,l,mid,x,y),ask(root<<1|1,mid+1,r,x,y));
}
ll query(int root,int l,int r,int x,int y)
{
if(l>=x&&r<=y)
return sum[root];
int mid=(l+r)>>1;
pushdown(root,l,r,mid);
if(y<=mid) return query(root<<1,l,mid,x,y);
if(x>mid) return query(root<<1|1,mid+1,r,x,y);
return query(root<<1,l,mid,x,y)+query(root<<1|1,mid+1,r,x,y);
}
int main()
{
int n,m,i,x,l,r,op;
scanf("%d%d",&n,&m);
for(i=1;i<=n;++i)
scanf("%lld",&a[i]);
build(1,1,n);
for(i=1;i<=m;++i)
{
scanf("%d%d%d",&op,&l,&r);++l,++r;
if(op==1) scanf("%d",&x),Add(1,1,n,l,r,x);
else if(op==2) scanf("%d",&x),modify(1,1,n,l,r,x);
else if(op==3) printf("%lld\n",ask(1,1,n,l,r));
else printf("%lld\n",query(1,1,n,l,r));
}
return 0;
}