首页 > 其他分享 >小清新线段树

小清新线段树

时间:2024-02-20 22:44:49浏览次数:30  
标签:int 线段 long seg 复杂度 清新 define

小清新线段树

定义

结合时间复杂度分析(势能分析)以及懒标记应用的非传统线段树

可以理解为带剪枝的线段树

复杂度证明

以 The Child and Sequence 为例,先看操作 1,2,对于一个数 \(x\) 进行取模,要么这个数保持不变。若模数 \(M>\frac{x}{2}\),则剩余部分小于 \(\frac{x}{2}\);若模数 \(M<\frac{x}{2}\),则剩余部分小于 \(M\)。综上x至少折半,那么这个数最多被取模 \(\log V\) 次。

在具体实现时,维护区间最大值,若最大值小于模数,则跳过此区间。

对于操作3,只会增加一个数的值,最多增加 \(O(\log V)\) 的复杂度

对一个点修改 \(O(\log n)\),由于多次修改会同时进行,LCA会多算,实际复杂度更小

综上时间复杂度 \(O((n+m)\log n\log V)\) ,空间复杂度(动态开点): \(O(n)\)

实现

维护区间值,进行剪枝

// Problem: The Child and Sequence
// Contest: Luogu
// URL: https://www.luogu.com.cn/problem/CF438D
// Memory Limit: 250 MB
// Time Limit: 4000 ms
// 
// Powered by CP Editor (https://cpeditor.org)

#include<bits/stdc++.h>
#define INF 0x3f3f3f3f
#define EPS 1e-8
#define ll long long
#define ld long double
#define PII pair<int,int>
#define PDD pair<int,int>
#define max3(a,b,c) max(max(a,b),c)
#define max4(a,b,c,d) max(max3(a,b,c),d)
#define lowbit(x) ((x)&-(x))
#define rep(i,a,b) for(int i=(a);i<=(b);++i)
#define Yes printf("Yes")
#define No printf("No")
#define chmax(a,b) (a=max(a,b))
#define chmin(a,b) (a=min(a,b))
using namespace std;
const int N=1e5+10;
int a[N],n,m;
struct SEG{
	struct SEG_Node{
		int ls,rs;
		long long sum;
		int maxv;
		#define lc t[p].ls
		#define rc t[p].rs
		#define mid (L+R>>1)
	}t[N<<1];
	int tot,rt;
	int mknode(){
		++tot;
		t[tot].ls = t[tot].rs = 0;
		t[tot].sum = t[tot].maxv = 0;
		return tot;
	}
	void pushup(int p){
		t[p].sum = t[lc].sum + t[rc].sum;
		t[p].maxv = max(t[lc].maxv,t[rc].maxv);
	}
	void build(int &p,int L,int R){
		if(!p)p=mknode();
		if(L==R){
			t[p].sum = t[p].maxv = a[L];
			return;
		}
		build(lc,L,mid);
		build(rc,mid+1,R);
		pushup(p);
	}
	void modify(int p,int L,int R,int l,int r,int x){
		if(L==R){
			t[p].sum = t[p].maxv = t[p].sum%x;
			return;
		}
		if(l<=mid&&t[lc].maxv>=x)modify(lc,L,mid,l,r,x);
		if(r>mid&&t[rc].maxv>=x)modify(rc,mid+1,R,l,r,x);
		pushup(p);
	}
	long long query(int p,int L,int R,int l,int r){
		if(l<=L&&R<=r){
			return t[p].sum;
		}
		long long res=0;
		if(l<=mid)res+=query(lc,L,mid,l,r);
		if(r>mid)res+=query(rc,mid+1,R,l,r);
		return res;
	}
	void change(int p,int L,int R,int k,int x){
		if(L==R){
			t[p].sum = t[p].maxv = x;
			return;
		}
		if(k<=mid)change(lc,L,mid,k,x);
		else change(rc,mid+1,R,k,x);
		pushup(p);
	}
}seg;
int main(){
	cin>>n>>m;
	for(int i=1;i<=n;++i)cin>>a[i];
	seg.build(seg.rt,1,n);
	for(int i=1;i<=m;++i){
		int op,l,r,x,k;cin>>op;
		if(op==1){
			cin>>l>>r;
			cout<<seg.query(seg.rt,1,n,l,r)<<'\n';
		}
		else if(op==2){
			cin>>l>>r>>x;
			seg.modify(seg.rt,1,n,l,r,x);
		}
		else{
			cin>>k>>x;
			seg.change(seg.rt,1,n,k,x);
		}
	}
	return 0;
}

思想总结

这里用到势能分析和均摊的思想。线段树灵活的区间处理优势被运用得淋漓尽致。这里没有用到懒标记,因为取模操作无法区间进行。小清新线段树的关键技术在于二进制划分区间后,能够通过整块的标记,进行类似剪枝的操作,减少时间的浪费。

线段树的算法,永无止境。当我第一次学小清新线段树的时候,就为之一振。原来,未来的时代永远充满创造力,充满活力。

练习题

The Child and Sequence

标签:int,线段,long,seg,复杂度,清新,define
From: https://www.cnblogs.com/life-of-a-libertine/p/18024212

相关文章

  • 线段树
    线段树是一种基于分治思想的二叉树结构,用于在区间上进行信息统计。线段树的每个节点都代表一个区间线段树具有唯一的根节点,代表的区间是整个统计范围,如[1,n]线段树的每个叶节点都代表一个长度为1的元区间对于每个内部节点[l,r],它的左子节点是[l,mid],右子节点是[mid+1,r],其中m......
  • 线段树—模板
    线段树常见操作build建树update更新query查询pushup向上回溯pushdown向下延迟更新(延迟标记)建线段树://预编译命令,做符号代换#definelson(gjd<<1)#definerson(gjd<<1|1)//gjd表示当前结点,[l,r]表示区间范围voidbuild(intgjd,intl,intr){tree[gjd]......
  • [COGS 755]山海经:线段树
    这是一道美妙的线段树板子,能够有效地提升我们的读题,理解,思考和代码能力;综上,这是一道大模拟显然,对于这道题的数据范围,直接暴力是行不通的,只能拿30分:30分暴力#include<bits/stdc++.h>usingnamespacestd;constintN=1000005;constintinf=0x7fffffff;structtree{ int......
  • 三哼经(山海经) 线段树
    关于这道题,网上的题解基本都是什么求最大连续子段和,还有什么最大前缀、最大后缀,看了半天也是实在看不明白,便找了个题解,开始给题解写注释(众所周知题解里基本没有注释doge),写了一下午加一晚上,倒是差不多明白了,又肝了0.3坤天终于把三哼经拿下了。题目巴拉巴拉说了一堆没用的的话,让......
  • 线段树-山海经 题解
    《最痛苦的一集》从开始的找维护变量到依据i比较依据y比较最后发现问题出在没初始化(如果不将答案赋值为极小值那么它最小值就是0因此wa了无数遍下面是思路首先要维护的变量有:\(区间的左右边界\,l,\,r\)\(区间的答案\,ans\)\(含左端点最大值\,lans\,和含右端点最......
  • 线段树
    线段树算是树状数组的进阶版了,树状数组能做的,线段树也肯定能做,它做不了的,线段树也能做。但确实线段树的代码量也很让人挠头,基本原理不再解释。先看一下基础的模板吧单点修改和区间查询点击查看代码#include<bits/stdc++.h>usingnamespacestd;constintN=300000;intn......
  • "山海经“ 讲解----线段树
    ”山海经“--线段树讲解1、题面:http://cogs.pro/cogs/problem/problem.php?pid=7752、题目大意及分析:i:大概就是说给了你一段[1,n]的区间,并给了每个区间的权值,下面会有m个问题,每个问题给你一段[1,n]的子区间[i,j],问你在这段区间上的任意一端子区间和最大是多少,并且要求输出这......
  • 线段树的各种板板~(*^▽^*)
    $\color{purple}{板板}$#include<bits/stdc++.h>usingnamespacestd;#defineintlonglong#defineinf0x3f#defineINF0x3f3f3f3f#definemst(a,b)memset(a,b,sizeof(a))#defineElaina0#definelid(id<<1)#definerid(id<<1|1)//即ridco......
  • 线段树模板
    开局宏定义:#include<bits/stdc++.h>#defineintlonglong#definelson(now<<1)//现结点的左孩子#definerson(now<<1|1)//右孩子usingnamespacestd;结构体定义:structNode{intl,r;//表示左右区间intmax,sum;//其他数据域}tree[N<<2]//=N*......
  • 线段树(板子)
    线段树单点修改,单点,区间查询区间修改,单点,区间查询单点修改普通线段树code#definels(rt<<1)#definers(rt<<1|1)#definellllonglongconstintN=1000001;intn,m;inta[N];structT{ intl,r,data;}tr[N<<2];voidpushup(intrt){ tr[rt].da......