LOJ6029 市场 和 UOJ228 基础数据结构练习题

区间除法、区间开根这类套路题。虽然实现很简单,但是复杂度证明比较复杂。

LOJ6029 市场

从前有一个贸易市场,在一位执政官到来之前都是非常繁荣的,自从他来了之后,发布了一系列奇怪的政令,导致贸易市场的衰落。

(n) 个商贩,从 (0 sim n - 1) 编号,每个商贩的商品有一个价格 (a_i),有两种政令;同时,有一个外乡的旅客想要了解贸易市场的信息,有两种询问方式:

  1. (政令)(l, r, c),对于 (i in [l, r], a_i leftarrow a_i + c)

  2. (政令)(l, r, d),对于 (i in [l, r], a_i leftarrow lfloor {a_i}/{d} floor)

  3. (询问)给定 (l, r),求 (min_{i in [l, r]} a_i)

  4. (询问)给定 (l, r),求 (sum_{iin [l, r]} a_i)

对于 (100\%) 的数据,(1 leq n, q leq 10 ^ 5, 0 leq l leq r leq n - 1, c in [-10 ^ {4}, 10 ^ 4], d in [2, 10 ^ 9])

题解

https://www.cnblogs.com/GXZlegend/p/8715279.html

对于原来的两个数 a 和 b ( a>b ) ,原来的差是 a-b ,都除以 d 后的差是 (frac{a-b}d) ,相当于差也除了 d 。

而当区间差为 0 或 a=kd,b=kd-1 的 1 时,区间下取整除就变成了区间减。

因此当一个区间下取整除了 (log(Max-Min)) 次后就不需要暴力下取整除,直接区间减即可。

定义线段树节点势能为 (log(Max-Min)) ,那么每次对 [l,r] 下取整除就是将所有 (lle x,yle r) ,且势能不为 0 的节点 [x,y] 的势能减 1 ,代价为势能减少总量。

分析区间加操作:只会修改到经过的节点的势能,影响 (log) 个节点,将这些点的势能恢复为 (log(Max-Min))

因此总的时间复杂度就是总势能量 (O((n+mlog n)log a))

注意C++中的 '/' 并不是下取整,而是向0取整,因此需要按正负分类讨论手写下取整。或者直接用浮点数的floor

struct node {int min,max;int64 sum;};
IN node operator*(CO node&a,CO node&b){ // merge
	return {min(a.min,b.min),max(a.max,b.max),a.sum+b.sum};
}
IN node operator+(CO node&a,CO node&b){
	return {a.min+b.min,a.max+b.max,a.sum+b.sum};
}

CO int N=1e5+10;
node tree[4*N];
int tag[4*N];

#define lc (x<<1)
#define rc (x<<1|1)
#define mid ((l+r)>>1)
void build(int x,int l,int r){
	if(l==r){
		int v=read<int>();
		tree[x]={v,v,v};
		return;
	}
	build(lc,l,mid),build(rc,mid+1,r);
	tree[x]=tree[lc]*tree[rc];
}
void push_down(int x,int l,int r){
	if(tag[x]){
		tree[lc]=tree[lc]+(node){tag[x],tag[x],(int64)tag[x]*(mid-l+1)},tag[lc]+=tag[x];
		tree[rc]=tree[rc]+(node){tag[x],tag[x],(int64)tag[x]*(r-mid)},tag[rc]+=tag[x];
		tag[x]=0;
	}
}
void ins_add(int x,int l,int r,int ql,int qr,int v){
	if(ql<=l and r<=qr){
		tree[x]=tree[x]+(node){v,v,(int64)v*(r-l+1)},tag[x]+=v;
		return;
	}
	push_down(x,l,r);
	if(ql<=mid) ins_add(lc,l,mid,ql,qr,v);
	if(qr>mid) ins_add(rc,mid+1,r,ql,qr,v);
	tree[x]=tree[lc]*tree[rc];
}
void ins_div(int x,int l,int r,int ql,int qr,int v){
	if(ql<=l and r<=qr){
		if(-1<=tree[x].min and tree[x].max<=0) return;
		if(l==r){
			int w=floor((float128)tree[x].min/v);
			tree[x]={w,w,w};
			return;
		}
		push_down(x,l,r);
		if(floor((float128)tree[x].min/v)-tree[x].min==floor((float128)tree[x].max/v)-tree[x].max){
			int delta=floor((float128)tree[x].min/v)-tree[x].min;
			tree[x]=tree[x]+(node){delta,delta,(int64)delta*(r-l+1)},tag[x]+=delta;
			return;
		}
		ins_div(lc,l,mid,ql,qr,v);
		ins_div(rc,mid+1,r,ql,qr,v);
		tree[x]=tree[lc]*tree[rc];
		return;
	}
	push_down(x,l,r);
	if(ql<=mid) ins_div(lc,l,mid,ql,qr,v);
	if(qr>mid) ins_div(rc,mid+1,r,ql,qr,v);
	tree[x]=tree[lc]*tree[rc];
}
node query(int x,int l,int r,int ql,int qr){
	if(ql<=l and r<=qr) return tree[x];
	push_down(x,l,r);
	if(qr<=mid) return query(lc,l,mid,ql,qr);
	if(ql>mid) return query(rc,mid+1,r,ql,qr);
	return query(lc,l,mid,ql,qr)*query(rc,mid+1,r,ql,qr);
}
#undef lc
#undef rc
#undef mid

int main(){
	int n=read<int>(),m=read<int>();
	build(1,1,n);
	while(m--){
		int opt=read<int>();
		if(opt==1){
			int l=read<int>()+1,r=read<int>()+1,c=read<int>();
			ins_add(1,1,n,l,r,c);
		}
		else if(opt==2){
			int l=read<int>()+1,r=read<int>()+1,d=read<int>();
			ins_div(1,1,n,l,r,d);
		}
		else if(opt==3){
			int l=read<int>()+1,r=read<int>()+1;
			node x=query(1,1,n,l,r);
			printf("%d
",x.min);
		}
		else{
			int l=read<int>()+1,r=read<int>()+1;
			node x=query(1,1,n,l,r);
			printf("%lld
",x.sum);
		}
	}
	return 0;
}

UOJ228 基础数据结构练习题

sylvia 是一个热爱学习的女孩子,今天她想要学习数据结构技巧。

在看了一些博客学了一些姿势后,她想要找一些数据结构题来练练手。于是她的好朋友九条可怜酱给她出了一道题。

给出一个长度为 (n) 的数列 (A),接下来有 (m) 次操作,操作有三种:

对于所有的 (i in [l,r]),将 (A_i) 变成 (A_i+x)
对于所有的 (i in [l,r]),将 (A_i) 变成 (lfloor sqrt {A_i} floor)
对于所有的 (i in [l,r]),询问 (A_i) 的和。
作为一个不怎么熟练的初学者,sylvia 想了好久都没做出来。而可怜酱又外出旅游去了,一时间联系不上。于是她决定向你寻求帮助:你能帮她解决这个问题吗。

对于所有数据,保证有 (n leq 100000)(m leq 100000)(1 leq l_i leq r_i leq n,1 leq A_i,x_i leq 10^5)

题解

https://www.cnblogs.com/GXZlegend/p/8709518.html

对于原来的两个数 a 和 b ( a>b ) ,开根后变成 (sqrt a)(sqrt b) ,它们的差从 a-b 变成了 (sqrt a-sqrt b)

又有 ((sqrt a-sqrt b)(sqrt a+sqrt b)=a-b) ,因此开方后的差小于原来差的开方。

而当区间差为 0 或 (a=x^2,b=x^2-1) 的 1 时,区间开根就变成了区间减。

因此一个区间开根 (loglog(Max-Min)) 次后就不需要暴力开根,直接区间减即可。

定义线段树节点势能为 (loglog(Max-Min)) ,那么每次对 [l,r] 开根就是将所有 (lle x,yle r) ,且势能不为 0 的节点 [x,y] 的势能减 1 ,代价为势能减少总量。

分析区间加操作:只会修改到经过的节点的势能,影响 (log) 个节点,将这些点的势能恢复为 (loglog(Max-Min))

因此总的时间复杂度就是总势能量 (O((n+mlog n)loglog a))

struct node {int64 min,max,sum;};
IN node operator*(CO node&a,CO node&b){ // merge
	return {min(a.min,b.min),max(a.max,b.max),a.sum+b.sum};
}
IN node operator+(CO node&a,CO node&b){
	return {a.min+b.min,a.max+b.max,a.sum+b.sum};
}

CO int N=1e5+10;
node tree[4*N];
int64 tag[4*N];

#define lc (x<<1)
#define rc (x<<1|1)
#define mid ((l+r)>>1)
void build(int x,int l,int r){
	if(l==r){
		int64 v=read<int64>();
		tree[x]={v,v,v};
		return;
	}
	build(lc,l,mid),build(rc,mid+1,r);
	tree[x]=tree[lc]*tree[rc];
}
void push_down(int x,int l,int r){
	if(tag[x]){
		tree[lc]=tree[lc]+(node){tag[x],tag[x],tag[x]*(mid-l+1)},tag[lc]+=tag[x];
		tree[rc]=tree[rc]+(node){tag[x],tag[x],tag[x]*(r-mid)},tag[rc]+=tag[x];
		tag[x]=0;
	}
}
void ins_add(int x,int l,int r,int ql,int qr,int64 v){
	if(ql<=l and r<=qr){
		tree[x]=tree[x]+(node){v,v,v*(r-l+1)},tag[x]+=v;
		return;
	}
	push_down(x,l,r);
	if(ql<=mid) ins_add(lc,l,mid,ql,qr,v);
	if(qr>mid) ins_add(rc,mid+1,r,ql,qr,v);
	tree[x]=tree[lc]*tree[rc];
}
void ins_sqrt(int x,int l,int r,int ql,int qr){
	if(ql<=l and r<=qr){
		if(tree[x].max==1) return;
		if(l==r){
			int64 w=floor(sqrt(tree[x].min));
			tree[x]={w,w,w};
			return;
		}
		push_down(x,l,r);
		if(floor(sqrt(tree[x].min))-tree[x].min==floor(sqrt(tree[x].max))-tree[x].max){
			int64 delta=floor(sqrt(tree[x].min))-tree[x].min;
			tree[x]=tree[x]+(node){delta,delta,delta*(r-l+1)},tag[x]+=delta;
			return;
		}
		ins_sqrt(lc,l,mid,ql,qr);
		ins_sqrt(rc,mid+1,r,ql,qr);
		tree[x]=tree[lc]*tree[rc];
		return;
	}
	push_down(x,l,r);
	if(ql<=mid) ins_sqrt(lc,l,mid,ql,qr);
	if(qr>mid) ins_sqrt(rc,mid+1,r,ql,qr);
	tree[x]=tree[lc]*tree[rc];
}
node query(int x,int l,int r,int ql,int qr){
	if(ql<=l and r<=qr) return tree[x];
	push_down(x,l,r);
	if(qr<=mid) return query(lc,l,mid,ql,qr);
	if(ql>mid) return query(rc,mid+1,r,ql,qr);
	return query(lc,l,mid,ql,qr)*query(rc,mid+1,r,ql,qr);
}
#undef lc
#undef rc
#undef mid

int main(){
	int n=read<int>(),m=read<int>();
	build(1,1,n);
	while(m--){
		int opt=read<int>();
		if(opt==1){
			int l=read<int>(),r=read<int>();
			ins_add(1,1,n,l,r,read<int64>());
		}
		else if(opt==2){
			int l=read<int>(),r=read<int>();
			ins_sqrt(1,1,n,l,r);
		}
		else{
			int l=read<int>(),r=read<int>();
			node x=query(1,1,n,l,r);
			printf("%lld
",x.sum);
		}
	}
	return 0;
}
原文地址:https://www.cnblogs.com/autoint/p/12761289.html