并查集维护后缀加
RT,设计一种数据结构,支持以下操作:
- 在末尾插入数字 \(k\)。
- 设当前加入了 \(len\) 个数字,给定 \(p,k\),将 \([p,len]\) 全部加上 \(k\)。\(k\) 是任意整数。
- 查询全局最大值以及最大值第一次出现位置。
可以使用并查集维护单调栈。
实现思路:使用并查集查询栈内 \(\ge p\) 的首个元素。
同时维护栈上的差分数组 \(c\),栈内元素 \(x\) 的真实值为栈内所有深度 $\le $ 它的 \(c\) 值之和加上它的初始值。
栈用链表维护。
栈底就是所求最值。
struct dsu{
int f[N],c[N],top,st,len,a[N];
int pos[N],pre[N],nxt[N];
void init(){
for(int i=0;i<=n;++i)f[i]=c[i]=pre[i]=pos[i]=nxt[i]=0,a[i]=-inf;
len=st=top=0;
}
int find(int x){return x==f[x]?x:f[x]=find(f[x]);}
void ins(int k,int i){
f[i]=i;a[i]=k;
int now=0;len=i;
int p=i-1;pre[i]=p;nxt[p]=i;
while(p){
if(a[p]+now+c[p]<a[i]){
now+=c[p];
f[p]=p+1;
pre[i]=pre[p];
if(pre[p])nxt[pre[p]]=i;
p=pre[i];
}
else break;
}
c[pre[i]]+=now;
if(!pre[i])st=i;
}
void add(int k,int val){
int p=find(k);
c[len]+=val;
c[pre[p]]-=val;int now=0;
while(pre[p]&&a[pre[p]]+c[pre[p]]+now<a[p]){
now+=c[pre[p]];
f[pre[p]]=pre[p]+1;
pre[p]=pre[pre[p]];
if(pre[p])nxt[pre[p]]=p;
}
c[pre[p]]+=now;
if(!pre[p])st=p;
}
pr val(){
return mk(a[st]-c[pre[st]],st);
}
}d;
复杂度瓶颈在并查集,因此可以使用严格线性并查集(类似于线性RMQ,使用 64 压位,每一位表示当前位置是否向左合并)优化到 \(O(n)\)。
每次只需要将一个位置置为 \(1\),以及跳过全 \(1\) 的并查集位置,支持查询二进制数的最低位的 \(1\) 即可。
题目:P10181 龙逐千灯幻

浙公网安备 33010602011771号