势能

1、势能线段树

势能线段树,即在线段树上进行有效操作会消耗势能,通过均摊复杂度使得每次暴力更新的复杂度正确。

常见势能操作:

  • 开根,取模(取模一次至少折半)
  • 按位与,按位或(每个数有 \(log\) 级别的二进制位)
  • 取 gcd(因为一个数的质因子数量是 \(log\) 级的,取一次 \(gcd\) 至少少 1 质因子)

在线段树区间上操作时需维护势能 \(tag\) 判断是否需要递归下去操作

P4145 上帝造题的七分钟2维护区间最大值

P9989 TEST_69:维护区间 \(lcm\)。但是可能区间 \(lcm\) 过大导致爆 long long ,此时令 \(lcm\) 取 inf 就行。感觉自己想了话能只想到用 bitset 维护质因子,复杂度正确性全没,纯菜

2、平衡树有交合并

CF原文链接

假设现在要合并序列 \(a,b\) ,而 \(a,b\) 的值域分布如下图

           <-d1->       <-d2->         <d3->     <d4>
a:{[--a1--]                  [---a2---]              [--a3--] }
b:{             [--b1--]                   [-b2-]             }

那么每次操作相当于是取一个连续前缀并到最终的序列 \(c\)

令有序序列 \(a\) 的势能函数为 \(f(a)=\sum{log(a_{i+1}-a_i)}\),那么合并前后 \(a\)\(c\) 的势能差即

\[\Delta=\sum{log(d_i+d_{i+1}+b_i)}-\sum{log(d_i)} \]

忽略 \(b_i\) 的贡献,由 \(log(x+y)\geqslant 1+\frac{log(x)+log(y)}{2}\) 可得 \(\Delta\geqslant k-\frac{log(d_1)+log(d_k)}{2}\),即势能减小量为 \(O(k)\) 级别的(其中 \(k\) 是序列 \(d\) 的连续段个数)

设值域为 \(V\),初始势能上限为 \(n\log V\),每次合并的复杂度为 \(O(k\log n)\),那么总的复杂度就是 \(\frac{n\log V}{k}\times k\log n\),即 \(O(n\log n\log V)\)

代码:

int Merge(int lrt,int rrt)
{
    if (!lrt||!rrt) return lrt^rrt;
    push_down(lrt),push_down(rrt);
    int l,r; split(lrt,tr[rrt].mn,l,r);
    return merge(l,Merge(rrt,r));
}

P8264 TEST_100

插入标记回收算法:离线处理所有操作 \(x\leftarrow F_i(x),i\in[l,r]\),每次操作在 \(l\) 处插入,\(r\) 处查询,在 \(i\) 对所有 \(x_j\) 进行操作 \(x_j\leftarrow F_i(x_j)\)

在这道题中,\(F_i(x)=|x-a_i|\),分类考虑 \(x\in[0,a_i]\)\(x\in(a_i,V]\) 即可,用平衡树可以快速操作。因为每次操作后两棵平衡树的值域有交,所以需要用有交合并

但是这道题还要求强制在线。区间问题,考虑分块预处理出 \([0,V]\) 经过每个块的答案,然后没了。当块长取 \(B=\sqrt{n}\log n\log V\) 时最优

代码
#include <bits/stdc++.h>
using namespace std;
const int N=1e5+5;
const int B=5000;
int n,m,a[N];
int pos[N],l[N],r[N];
int f[22][N],lst;
struct FHQ_Treap
{
    struct node{
        int ls,rs;
        int val,mn,mx;
        int pri,tag1,tag2;
    }tr[N];
    int cnt=0,rt;
    inline int newnode(int x) { tr[++cnt]={0,0,x,x,x,rand(),0,0}; return cnt; }
    inline void upd(int id)
    {
        if (!id) return ;
        swap(tr[id].mn,tr[id].mx);
        swap(tr[id].ls,tr[id].rs);
        tr[id].tag1^=1; tr[id].tag2*=-1;
        tr[id].mn*=-1,tr[id].mx*=-1,tr[id].val*=-1;
    }
    inline void push_down(int id)
    {
        if (tr[id].tag1) { upd(tr[id].ls),upd(tr[id].rs); tr[id].tag1=0; }
        if (tr[id].tag2)
        {
            int ls=tr[id].ls,rs=tr[id].rs,k=tr[id].tag2;
            tr[ls].mn+=k,tr[ls].mx+=k,tr[ls].val+=k;
            tr[rs].mn+=k,tr[rs].mx+=k,tr[rs].val+=k;
            tr[ls].tag2+=k,tr[rs].tag2+=k,tr[id].tag2=0;
        }
    }
    inline void push_up(int id)
    {
        tr[id].mn=tr[id].mx=tr[id].val;
        if (tr[id].ls)
        {
            tr[id].mn=min(tr[id].mn,tr[tr[id].ls].mn);
            tr[id].mx=max(tr[id].mx,tr[tr[id].ls].mx);
        }
        if (tr[id].rs)
        {
            tr[id].mn=min(tr[id].mn,tr[tr[id].rs].mn);
            tr[id].mx=max(tr[id].mx,tr[tr[id].rs].mx);
        }
    }
    void push_tag(int id)
    {
        if (!id) return ;
        push_down(id);
        push_tag(tr[id].ls),push_tag(tr[id].rs);
    }
    void split(int id,int x,int &l,int &r)
    {
        if (!id) { l=r=0; return ; }
        push_down(id);
        if (tr[id].val<=x) { l=id; split(tr[id].rs,x,tr[id].rs,r); }
        else { r=id; split(tr[id].ls,x,l,tr[id].ls); }
        push_up(id);
    }
    int merge(int lrt,int rrt)
    {
        if (!lrt||!rrt) return lrt^rrt;
        push_down(lrt); push_down(rrt);
        if (tr[lrt].pri>tr[rrt].pri)
        {
            tr[lrt].rs=merge(tr[lrt].rs,rrt);
            push_up(lrt); return lrt;
        }
        else
        {
            tr[rrt].ls=merge(lrt,tr[rrt].ls);
            push_up(rrt); return rrt;
        }
    }
    int Merge(int lrt,int rrt)
    {
        if (!lrt||!rrt) return lrt^rrt;
        push_down(lrt),push_down(rrt);
        int l,r; split(lrt,tr[rrt].mn,l,r);
        return merge(l,Merge(rrt,r));
    }
    inline void insert() { for (int i=0;i<=100000;i++) rt=merge(rt,newnode(i)); }
    inline void query(int id)
    {
        push_tag(rt);
        for (int i=0;i<=100000;i++) f[id][i]=tr[i+1].val;
        rt=cnt=0; insert();
    }
    void ea(int id)
    {
        if (!id) return ;
        ea(tr[id].ls); cout<<id-1<<"-"<<tr[id].val<<" ";
        ea(tr[id].rs);
    }
    inline void update(int k)
    {
        int l,r;
        split(rt,k,l,r); 
        tr[l].tag2-=k; tr[r].tag2-=k; upd(l); 
        tr[l].val+=k; tr[l].mn+=k; tr[l].mx+=k; 
        tr[r].val-=k; tr[r].mn-=k; tr[r].mx-=k;
        rt=Merge(l,r);
    }
}Tp;

inline int fc(int l,int r,int v)
{
    for (int i=l;i<=r;i++) v=abs(v-a[i]);
    return v;
}
inline int solve(int ll,int rr,int v)
{
    if (rr-ll+1<=B) return fc(ll,rr,v);
    int lid=pos[ll],rid=pos[rr],ff=0;
    if (l[ll]!=ll) { lid++; for (int i=ll;i<=r[ll];i++) v=abs(v-a[i]); }
    if (r[rr]!=rr) { ff=1; rid--; }
    for (int i=lid;i<=rid;i++) v=f[i][v];
    if (ff) { for (int i=l[rr];i<=rr;i++) v=abs(v-a[i]); }
    return v;
}
int main()
{
    ios::sync_with_stdio(0);
    cin.tie(0),cout.tie(0);

    cin>>n>>m;
    for (int i=1;i<=n;i++) cin>>a[i];

    Tp.insert();
    for (int i=1;i<=n;i++)
    {
        pos[i]=(i+B-1)/B;
        l[i]=(pos[i]-1)*B+1;
        r[i]=min(n,pos[i]*B);
        Tp.update(a[i]);
        if (r[i]==i) Tp.query(pos[i]);
    }   

    int l,r,v;
    while (m--)
    {
        cin>>l>>r>>v;
        l^=lst; r^=lst; v^=lst;
        lst=solve(l,r,v);
        cout<<lst<<"\n";
    }
    return 0;
}
posted @ 2025-05-05 22:07  沄沄沄  阅读(34)  评论(3)    收藏  举报