题目
解法
由于有区间加,直接暴力做除法的复杂度是不对的。
不过我们可以尝试将其转化为区间加法:若对于某个区间的 (p=max,q=min),有
[Delta =p-left lfloorfrac{p}{d}
ight
floor=q-left lfloorfrac{q}{d}
ight
floor
]
那么整个区间相当于减去 (Delta)。
具体可以从这个角度理解:(y=x) 的增长速度比 (y=left lfloorfrac{x}{d} ight floor,dge 1) 更快,所以当 (x) 变大时,(Delta) 也在变大。
但如果不满足这个条件,复杂度看似仍然会炸啊?
我们考虑用势能分析解决这个问题。定义一个代表 ([l,r]) 区间的节点的势能为 (log(max_{l,r}-min_{l,r}))。整棵树的势能就是所有节点的势能之和。
查询操作并不引起势能变化,而且复杂度是显然的,不在我们的考虑范围之内。
对于区间修改,暂时先考虑不完全被 ([l,r]) 覆盖的区间的节点的势能变化:单个节点最坏情况增加 (log V) 的势能((V) 是值域),这样的区间等价于分别包含 (l,r) 端点的区间,所以是 (log n) 级别的。那么总共只会增加 (qcdot log nlog V) 的势能。
对于完全被 ([l,r]) 覆盖的区间的节点,考虑一棵二叉树的节点个数大概是二倍值域大小,所以完全被 ([l,r]) 覆盖的区间的节点个数在 (r-l+1) 级别。需要分两种操作讨论:
- 区间加。显然势能是不变的。总时间复杂度 (mathcal O(qlog n))。
- 区间除。
- (max_{L,R}-min_{L,R}le 1)。设满足此条件的节点个数为 (t)。此时势能极小而且基本不变了,而且它大概率满足上文优化的条件。所以我们认为它是 (mathcal O(log n)) 级别的。
- (max_{L,R}-min_{L,R}> 1)。由于 (d>1),那么 (max_{L,R}-min_{L,R}) 至少减半,也即势能至少减 (1)。修改这部分节点复杂度是 (mathcal O(r-l+1-t)) 级别的,但同时势能至少减少 (r-l+1-t)。当减少到 (max_{L,R}-min_{L,R}le 1) 时,就又变成了上面的问题。
所以总复杂度应该也就是积蓄的最大势能:(mathcal O(qcdot log nlog V))。
代码
#include <cstdio>
#define print(x,y) write(x),putchar(y)
template <class T>
inline T read(const T sample) {
T x=0; char s; bool f=0;
while((s=getchar())>'9' or s<'0')
f|=(s=='-');
while(s>='0' and s<='9')
x=(x<<1)+(x<<3)+(s^48),
s=getchar();
return f?-x:x;
}
template <class T>
inline void write(const T x) {
if(x<0) {
putchar('-'),write(-x);
return;
}
if(x>9) write(x/10);
putchar(x%10^48);
}
#include <cmath>
#include <iostream>
using namespace std;
typedef long long ll;
const int maxn=1e5+5;
int a[maxn];
struct node {
ll s;
int tag,mx,mn;
} t[maxn<<2];
void pushUp(int o) {
t[o].s=t[o<<1].s+t[o<<1|1].s;
t[o].mx=max(t[o<<1].mx,t[o<<1|1].mx);
t[o].mn=min(t[o<<1].mn,t[o<<1|1].mn);
}
void build(int o,int l,int r) {
if(l==r) {
t[o].s=t[o].mx=t[o].mn=a[l];
return;
}
int mid=l+r>>1;
build(o<<1,l,mid);
build(o<<1|1,mid+1,r);
pushUp(o);
}
void pushDown(int o,int L,int R) {
if(!t[o].tag)
return;
int l=o<<1,r=o<<1|1,mid=L+R>>1;
t[l].s+=1ll*t[o].tag*(mid-L+1),t[r].s+=1ll*t[o].tag*(R-mid);
t[l].mx+=t[o].tag,t[r].mx+=t[o].tag;
t[l].mn+=t[o].tag,t[r].mn+=t[o].tag;
t[l].tag+=t[o].tag,t[r].tag+=t[o].tag;
t[o].tag=0;
}
void div(int o,int l,int r,int L,int R,int d) {
if(l>R or r<L) return;
if(l>=L and r<=R) {
int p=t[o].mx-(int)floor(1.0*t[o].mx/d);
int q=t[o].mn-(int)floor(1.0*t[o].mn/d);
if(p==q) {
t[o].s-=1ll*p*(r-l+1);
t[o].mx-=p,t[o].mn-=p;
t[o].tag-=p;
return;
}
}
int mid=l+r>>1;
pushDown(o,l,r);
div(o<<1,l,mid,L,R,d);
div(o<<1|1,mid+1,r,L,R,d);
pushUp(o);
}
void modify(int o,int l,int r,int L,int R,int d) {
if(l>R or r<L) return;
if(l>=L and r<=R) {
t[o].s+=1ll*(r-l+1)*d;
t[o].mn+=d,t[o].mx+=d;
t[o].tag+=d;
return;
}
int mid=l+r>>1;
pushDown(o,l,r);
modify(o<<1,l,mid,L,R,d);
modify(o<<1|1,mid+1,r,L,R,d);
pushUp(o);
}
ll ask(int o,int l,int r,int L,int R) {
if(l>R or r<L) return 0;
if(l>=L and r<=R) return t[o].s;
int mid=l+r>>1;
pushDown(o,l,r);
return ask(o<<1,l,mid,L,R)+
ask(o<<1|1,mid+1,r,L,R);
}
int m_ask(int o,int l,int r,int L,int R) {
if(l>R or r<L) return 2e9;
if(l>=L and r<=R) return t[o].mn;
int mid=l+r>>1;
pushDown(o,l,r);
return min(m_ask(o<<1,l,mid,L,R),
m_ask(o<<1|1,mid+1,r,L,R));
}
int main() {
int n=read(9),q=read(9);
for(int i=1;i<=n;++i)
a[i]=read(9);
build(1,1,n);
int op,l,r,x;
while(q--) {
op=read(9);
l=read(9)+1,r=read(9)+1;
if(op==1)
modify(1,1,n,l,r,read(9));
else if(op==2)
div(1,1,n,l,r,read(9));
else if(op==3)
print(m_ask(1,1,n,l,r),'
');
else print(ask(1,1,n,l,r),'
');
}
return 0;
}