势能
1、势能线段树
势能线段树,即在线段树上进行有效操作会消耗势能,通过均摊复杂度使得每次暴力更新的复杂度正确。
常见势能操作:
在线段树区间上操作时需维护势能 \(tag\) 判断是否需要递归下去操作
P4145 上帝造题的七分钟2:维护区间最大值
P9989 TEST_69:维护区间 \(lcm\)。但是可能区间 \(lcm\) 过大导致爆 long long ,此时令 \(lcm\) 取 inf 就行。感觉自己想了话能只想到用 bitset 维护质因子,复杂度正确性全没,纯菜
2、平衡树有交合并
假设现在要合并序列 \(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\) 的势能差即
忽略 \(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;
}

浙公网安备 33010602011771号