题解:P14676 [ICPC 2025 Seoul R] Mex Culpa

Solution

首先转化 \(a_i\le a_j+b_j,a_j\le a_i+b_i\) 的条件。因为 \(a_i< a_i+b_i\),所以对每个 \(i\) 可将 \(a_i,b_i\) 两个元素转化为区间 \(S_i=[a_i,a_i+b_i]\),那么满足上述条件的 \(j\) 只需满足区间 \(S_j\) 与区间 \(S_i\) 有交集。

如果我们以 \(i\) 从小到大处理,则需维护一个区间赋值、区间 \(\mathtt{mex}\) 的数据结构,然而这是很困难的。考虑到 \(\mathtt{mex}\) 的性质,我们不妨以 \(f_i\) 从小到大处理。

对于所有 \(f_i=0\),就是不存在满足 \(j<i\) 的区间 \(S_j\)\(S_i\) 有交,那么只需找到最小的编号,此时为 \(1\),则 \(f_1=0\),然后将区间 \(S_1\) 染色,找到未被染色的区间中的最小编号 \(i\),则 \(f_i=0\),并重复这一染色过程直至没有未被染色的区间,我们就找到所有 \(f_i=0\) 了。

但是维护未被染色的区间也很困难,那有没有办法不染色呢?发现所有 \(f_i=0\) 的区间 \(S_i\) 是相互无交集的,那么处理完 \(1\) 后直接对 \(S_1\) 左右两边分治,找到当前区间中的最小编号,然后继续分治就好了。其中,找 \(S_i\subset [L,R]\) 区间中的最小编号,就是找满足 \(a_i\ge L,a_i+b_i\le R\) 的最小编号,放到二维平面上用树套树维护最小值即可。

接着对于 \(f_i=1\),不难发现就是把所有 \(f_i=0\) 的区间删除后不存在满足 \(j<i\) 的区间 \(S_j\)\(S_i\) 有交,重复刚刚对 \(f_i=0\) 的操作即可。所以,找到所有 \(f_i=k\) 后,将这些区间在树套树维护的平面上对应的点删除,就能像处理 \(f_i=0\) 一样处理 \(f_i=k+1\) 了。

细节处理方面,由于存在端点相同的区间,即二维平面上横坐标或纵坐标相同的点,在维护最值的树套树上直接进行删除操作可能会出错。那么我们在离散化时把相同的端点给拆成不同的端点处理即可。

时空复杂度 \(\mathcal O(n\log^2 n)\)

Code

用了树状数组套线段树来实现

#include<bits/stdc++.h>
using namespace std;
namespace io{(快读)}using namespace io;
const int N=2.5e5+5,M=N*2;
int n,m,a[N],b[N],nc,c[M],f[N];
int tot,rt[M],mx[M*100],ls[M*100],rs[M*100];
void pushup(int u){mx[u]=max(mx[ls[u]],mx[rs[u]]);}
void T_upd(int y,int k,int&u,int l=1,int r=m){
    if(!u)u=++tot;
    if(l==r)return void(mx[u]=k);
    int mid=(l+r)/2;
    if(y<=mid)T_upd(y,k,ls[u],l,mid);
    else T_upd(y,k,rs[u],mid+1,r);
    pushup(u);
}
void BIT_upd(int x,int y,int k){x=m+1-x;for(;x<=m;x+=x&-x)T_upd(y,k,rt[x]);}
int T_ask(int p,int q,int u,int l=1,int r=m){
    if(!u)return 0;
    if(p<=l&&r<=q)return mx[u];
    int mid=(l+r)/2,res=0;
    if(p<=mid)res=max(res,T_ask(p,q,ls[u],l,mid));
    if(q>mid)res=max(res,T_ask(p,q,rs[u],mid+1,r));
    return res;
}
int BIT_ask(int l,int r){
    int x=m+1-l,y=r,res=0;
    for(;x;x-=x&-x)res=max(res,T_ask(1,y,rt[x]));
    return res;
}
int now,cnt,cur,mb,nxt[N],L[M],R[M],bel[M];
map<pair<int,int>,int>mp;
vector<int>V,ca[M],cb[M];
void solve(int l=1,int r=m){
    if(l>r)return;
    int id=BIT_ask(l,r);
    if(!id)return;
    id=n+1-id;
    f[id]=now;cnt++;
    V.push_back(id);
    solve(l,L[bel[a[id]]]-1);
    solve(R[bel[b[id]]]+1,r);
}
int main(){
    read(n);
    for(int i=1;i<=n;i++)read(a[i]),c[i]=a[i];
    for(int i=1;i<=n;i++)read(b[i]),c[n+i]=b[i]+=a[i];
    sort(c+1,c+2*n+1);
    nc=unique(c+1,c+2*n+1)-c-1;
    for(int i=1;i<=n;i++){
        a[i]=lower_bound(c+1,c+nc+1,a[i])-c;
        b[i]=lower_bound(c+1,c+nc+1,b[i])-c;
        ca[a[i]].push_back(i);//记录相同的端点
        cb[b[i]].push_back(i);
        mb=max(mb,b[i]);
    }memset(L,0x3f,sizeof(L));
    for(int i=1;i<=mb;i++){//拆成不同的端点
        for(int j:ca[i]){
            a[j]=++cur;
            bel[cur]=i;
            L[i]=min(L[i],cur);
            R[i]=max(R[i],cur);
        }for(int j:cb[i]){
            b[j]=++cur;
            bel[cur]=i;
            L[i]=min(L[i],cur);
            R[i]=max(R[i],cur);
        }m=max(m,cur);
    }
    for(int i=1;i<=n;i++)BIT_upd(a[i],b[i],n+1-i);
    for(;now<n;now++){
        solve();
        if(cnt>=n)break;
        for(int i:V)BIT_upd(a[i],b[i],0);
        V.clear();
    }for(int i=1;i<=n;i++)printf("%d ",f[i]);
    return 0;
}
posted @ 2025-12-26 15:23  ADay526  阅读(2)  评论(0)    收藏  举报