势能线段树+弩蚊怒夏(详细思路)

势能线段树

势能:

信息学中,势能被用于计算某一个过程,或者某一类过程时间复杂度的总和

例如计算两个数之间的最大公倍数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 (lir)(lir)
查 询 给 定 区 间 [ l , r ] 的 元 素 和 , 即 求 ∑ i = l r a i 查询给定区间[l,r]的元素和,即求\sum_{i=l}^{r}a_{i} [l,r]i=lrai
简单来说,通过记录区间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
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值