势能线段树
势能:
信息学中,势能被用于计算某一个过程,或者某一类过程时间复杂度的总和
例如计算两个数之间的最大公倍数gcd的时间复杂度是O(log N),而计算n个数之间的时间复杂度是O(n+log N)
总复杂度=数组循环遍历复杂度+gcd函数被调用的总复杂度
势能均摊复杂度
势能均摊复杂度是指把总的时间复杂度摊到操作次数或者循环次数上面
在上述n个数之间的gcd的时间复杂度为:
O ( N + l o g C ) N = O ( 1 + ( log C N ) ) = O ( 1 ) \frac{O(N+logC)}{N}=O(1+(\frac{\log C}{N}))=O(1) NO(N+logC)=O(1+(NlogC))=O(1)
则线段树维护区间gcd时间复杂度为O(nlogn)
有了上述势能知识可以更好地帮助我们计算时间复杂度
模板
链接:https://ac.nowcoder.com/acm/contest/19917/D
来源:牛客网
则势能线段树则总时间复杂度为{O(M \times |0势能时线段树操作时间复杂度|+N\times |节点势能上限降低至0势能时间复杂度|)}O(M×∣0势能时线段树操作时间复杂度∣+N×∣节点势能上限降低至0势能时间复杂度∣)。
使用势能线段树时要定义势能、势能初始值(势能最大值)、0势能点。
本题中有两种可以定义势能与0势能的方法,你可以都尝试一下。
定义区间开根次数cnt为势能,势能初始值为势能上限=6,定义0势能点为cnt=0。
定义区间最大值max为势能,势能初始值为区间最大值,定义0势能点为max=1。
给你一个长度大小为{N}N的正整数数组,进行{M}M次操作,操作有下列两种。
1. 给 定 区 间 [ l , r ] 对 区 间 中 所 有 数 字 开 根 号 向 下 取 整 , 即 a i = ⌊ a i ⌋ ( l ≤ i ≤ r ) ( l ≤ i ≤ r ) 。 1.给定区间[l,r]对区间中所有数字开根号向下取整,即a_i= \lfloor \sqrt{a_i} \rfloor(l \leq i \leq r)(l≤i≤r)。 1.给定区间[l,r]对区间中所有数字开根号向下取整,即ai=⌊ai⌋(l≤i≤r)(l≤i≤r)。
查 询 给 定 区 间 [ l , r ] 的 元 素 和 , 即 求 ∑ i = l r a i 查询给定区间[l,r]的元素和,即求\sum_{i=l}^{r}a_{i} 查询给定区间[l,r]的元素和,即求i=l∑rai
简单来说,通过记录区间min和max值,在进行开方的时候max和min是同步操作的。每一次开方都在加速是的max-min的值越来越小,max-min=0也就是势能零点。因此这里考虑类似于dfs中的剪枝,当max-min=0的情况下可以对区间进行统一操作:
#include<bits/stdc++.h>
using namespace std;
#define N 200010
#define int long long
typedef long long LL;
#define int long long
int n,m;
int w[N];
struct Node{
int l,r;
LL sum,max,min;
LL lazy,set; //set是区间置数
}tr[N<<4];
void push_up(int u){
tr[u].sum=tr[u<<1].sum+tr[u<<1|1].sum;
tr[u].min=min(tr[u<<1].min,tr[u<<1|1].min);
tr[u].max=max(tr[u<<1].max,tr[u<<1|1].max);
tr[u].lazy=tr[u].set=0;
}
void val(Node &u,int lazy,int set){
if(lazy){
u.sum+=lazy*(u.r-u.l+1);
u.max+=lazy;
u.min+=lazy;
if(u.set)
u.set+=lazy;
else
u.lazy+=lazy;
}
if(set){
u.sum=set*(u.r-u.l+1);
u.max=set;
u.min=set;
u.lazy=0;
u.set=set;
}
}
void push_down(int u){
val(tr[u<<1], tr[u].lazy, tr[u].set);
val(tr[u<<1|1], tr[u].lazy, tr[u].set);
tr[u].lazy=0,tr[u].set=0;
}
void build(int u,int l,int r){
tr[u]={
l,r};
if(l==r){
tr[u].sum=tr[u].max=tr[u].min=w[l];
tr[u].lazy=tr[u].set=0;
return ;
}
int mid= l+r >> 1;
build(u<<1,l,mid