题目

传送门

解法

由于有区间加,直接暴力做除法的复杂度是不对的。

不过我们可以尝试将其转化为区间加法:若对于某个区间的 \(p=\max,q=\min\),有

\[\Delta =p-\left \lfloor\frac{p}{d} \right \rfloor=q-\left \lfloor\frac{q}{d} \right \rfloor \]

那么整个区间相当于减去 \(\Delta\)

具体可以从这个角度理解:\(y=x\) 的增长速度比 \(y=\left \lfloor\frac{x}{d} \right \rfloor,d\ge 1\) 更快,所以当 \(x\) 变大时,\(\Delta\) 也在变大。

但如果不满足这个条件,复杂度看似仍然会炸啊?

我们考虑用势能分析解决这个问题。定义一个代表 \([l,r]\) 区间的节点的势能为 \(\log(\max_{l,r}-\min_{l,r})\)。整棵树的势能就是所有节点的势能之和。

查询操作并不引起势能变化,而且复杂度是显然的,不在我们的考虑范围之内。

对于区间修改,暂时先考虑不完全被 \([l,r]\) 覆盖的区间的节点的势能变化:单个节点最坏情况增加 \(\log V\) 的势能(\(V\) 是值域),这样的区间等价于分别包含 \(l,r\) 端点的区间,所以是 \(\log n\) 级别的。那么总共只会增加 \(q\cdot \log n\log V\) 的势能。

对于完全被 \([l,r]\) 覆盖的区间的节点,考虑一棵二叉树的节点个数大概是二倍值域大小,所以完全被 \([l,r]\) 覆盖的区间的节点个数在 \(r-l+1\) 级别。需要分两种操作讨论:

  • 区间加。显然势能是不变的。总时间复杂度 \(\mathcal O(q\log 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(q\cdot \log n\log 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),'\n');
		else print(ask(1,1,n,l,r),'\n');
	}
	return 0;
}
posted on 2021-08-23 21:01  Oxide  阅读(77)  评论(0编辑  收藏  举报