1. 程式人生 > >[CF1039E]Summer Oenothera Exhibition[根號分治+lct]

[CF1039E]Summer Oenothera Exhibition[根號分治+lct]

題意

給一個長度為 \(n\) 的序列, \(q\) 次詢問,次給一個 \(k_i\) ,問最少將序列劃分成多少次,滿足每一段的極差不超過\(w−k_i\).

\(1 \leq n, q \leq 10^5, 1 \leq w \leq 10^9,1 \leq k_i \leq w,0 \leq x_i \leq 10^9\)

分析

  • 每次直接貪心是正確的,可以考慮從第一段的影響證明,一定是儘量減少第二段的負擔。

  • \(k=w-k\) ,把詢問按照 \(k\) 排序,那麼段數顯然單調不升。

  • \({nxt}_i\) 表示 \(i\) 位置在當前詢問的 \(k\) 下合法的最遠位置 \(+1\)

    ,連邊 \(i \rightarrow {nxt}_i\)

  • 考慮在 \(k\) 變大的時候,我們修改一些位置的 \(nxt\),並使用 \(lct\) 加刪邊。如果 \({nxt}_i-i \geq \sqrt n\) 則不再連邊。
    查詢時如果走到了一棵樹的樹根便進行二分找到樹根的 \(nxt\) ,因為二分時一定至少跳了 \(\sqrt n\) 步,所以這樣的操作不會超過 \(\sqrt n\) 個。

  • 總時間複雜度為 \(O(n\sqrt nlogn)\)

  • 感覺這種按資料根號分類的題都是平衡了兩種暴力之間的複雜度

程式碼

#include<bits/stdc++.h>
using namespace std;
#define rep(i,a,b) for(int i=a;i<=b;++i)
#define pb push_back
typedef long long LL;
inline int gi(){
    int x=0,f=1;char ch=getchar();
    while(!isdigit(ch)) {if(ch=='-') f=-1;ch=getchar();}
    while(isdigit(ch)){x=(x<<3)+(x<<1)+ch-48;ch=getchar();}
    return x*f;
}
const int N=1e5 + 7;
int n,w,Q,sz;
int x[N],nxt[N],mi[N][20],mx[N][20],Log[N],ans[N];
struct qs{
    int k,id;
    bool operator <(const qs &rhs)const{
        return k<rhs.k;
    }
}q[N];
int fa[N],tr[N][2],son[N],rev[N];
#define pa fa[o]
#define Ls tr[o][0]
#define Rs tr[o][1]
bool isrt(int o){return tr[pa][0]^o&&tr[pa][1]^o;}
int side(int o){return tr[pa][1]==o;}
void reverse(int o){rev[o]^=1,swap(Ls,Rs);}
void pushup(int o){ son[o]=son[Ls]+son[Rs]+1; }
void rotate(int o){
    int f=pa,y=fa[pa],x=side(o),s=tr[o][x^1];
    if(!isrt(f)) tr[y][side(f)]=o;fa[o]=y;
    tr[f][x]=s,fa[s]=f;
    tr[o][x^1]=f,fa[f]=o;
    pushup(f),pushup(o);
}
void splay(int o){
    for(;!isrt(o);rotate(o))
    if(!isrt(pa)) rotate(side(o)==side(pa)?pa:o);
}
void access(int o){
    for(int y=0;o;y=o,o=pa) splay(o),Rs=y,pushup(o);
}
void link(int a,int b){
    access(a),splay(a);
    fa[a]=b;
}
void cut(int a,int b){
    access(a),splay(a);
    int x=tr[a][0];
    fa[x]=tr[a][0]=0;
    pushup(a);
}
int dep(int o){
    access(o);splay(o);
    return son[o];
}
int findr(int o){
    access(o);splay(o);
    while(Ls) o=Ls;
    return o;
}
vector<int>G[N];
int qmx(int l,int r){
    int k=Log[r-l+1];
    return max(mx[l][k],mx[r-(1<<k)+1][k]);
}
int qmi(int l,int r){
    int k=Log[r-l+1];
    return min(mi[l][k],mi[r-(1<<k)+1][k]);
}
int main(){
    n=gi(),w=gi(),Q=gi();sz=sqrt(n);
    
    rep(i,1,n) x[i]=mi[i][0]=mx[i][0]=gi();
    x[n+1]=mi[n+1][0]=mx[n+1][0]=2e9+1;
    
    Log[1]=0, rep(i,2,n+1) Log[i]=Log[i>>1]+1;
    for(int k=1;1<<k<=n+1;++k)
    for(int i=1;i+(1<<k)-1<=n+1;++i){
        mi[i][k]=min(mi[i][k-1],mi[i+(1<<k-1)][k-1]);
        mx[i][k]=max(mx[i][k-1],mx[i+(1<<k-1)][k-1]);
    }
    
    rep(i,1,Q){
        q[i].id=i,q[i].k=w-gi();
    }
    
    sort(q+1,q+1+Q);
    rep(i,1,n+1) son[i]=1,nxt[i]=i,G[1].pb(i);
    
    rep(i,1,Q){
        for(auto p:G[i]){
            if(i!=1) cut(p,nxt[p]);
            int j=nxt[p]+1;
            for(;j<=min(p+sz,n+1);++j) if(qmx(p,j)-qmi(p,j)>q[i].k) break;
            if(j==p+sz+1) continue;
            
            nxt[p]=j,link(p,j);
            int x=lower_bound(q+1,q+1+Q,(qs){qmx(p,nxt[p])-qmi(p,nxt[p]),0})-q;
            if(x!=Q+1) G[x].pb(p);
        }
        
        int &res=ans[q[i].id];
        for(int j=1;j<=n+1;){
            res+=dep(j),j=findr(j);
            if(j==n+1) break;
            
            int l=j+1,r=n+1;
            while(l<r){
                int mid=l+r>>1;
                if(qmx(j,mid)-qmi(j,mid)>q[i].k) r=mid;
                else l=mid+1;
            }
            j=l;
        }
    }
    rep(i,1,Q) printf("%d\n",ans[i]-2);
    return 0;
}