并查集维护后缀加

RT,设计一种数据结构,支持以下操作:

  1. 在末尾插入数字 \(k\)
  2. 设当前加入了 \(len\) 个数字,给定 \(p,k\),将 \([p,len]\) 全部加上 \(k\)\(k\) 是任意整数。
  3. 查询全局最大值以及最大值第一次出现位置。

可以使用并查集维护单调栈。

实现思路:使用并查集查询栈内 \(\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 龙逐千灯幻

posted @ 2026-03-16 22:05  spdarkle  阅读(3)  评论(0)    收藏  举报