zoukankan      html  css  js  c++  java
  • 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;
    }
    
  • 相关阅读:
    nginx 配置上传文件大小限制
    linux大文件截取某个日期后面的所有内容
    软件架构笔记 五
    软件架构笔记 四
    软甲架构笔记 三
    软件架构笔记 二
    软件架构笔记 一
    c# 生成的没用文件
    c# 两个软件传参
    c# 四则运算出错
  • 原文地址:https://www.cnblogs.com/autoint/p/12761289.html
Copyright © 2011-2022 走看看