CF2066B 题解

比赛结束时我发现自己只过了两题,我意识到我的脑子已经被可爱的线段树吃掉大半了。

我并没有注意到如果直接不选 \(0\) 答案只会减少 \(1\) 的性质。但是这题还是可以直接暴力做。

首先 \(>\) 全局 \(\mathrm{mex}\) 的可以直接全选。剩下的数要满足条件,根据定义有这么几种情况:

  • 直接不选。
  • 这一种数选一个,选完之后 \(\mathrm{mex} \le\) 他自己。符合条件。
  • 选多个,且选第一个的时候要满足 \(\le\) 他的所有数中至少有一个已经被选空了。此时我们发现既然这种数中选上第一个时满足该条件,那么后面的也肯定满足条件,所以选的是一个后缀。

这时直接设 \(f_{i,j}\) 为考虑了 \(0 \sim i\),此时选单点的数位置最靠右的在 \(j\) 时,最多选了几个数。

根据上面的定义就可以直接线段树解决了。

具体地,考虑 \(i\) 时:

  • 直接不选 \(i\),用 \(\max\limits_{j=1}^{n}{f_{i-1,j}} + \sum\limits_{j \ge i+1} cnt_j\) 更新答案。
  • 选了 \(a_j = i\)\(f_{i,j} = \max\limits_{k \ge j}{f_{i-1,k}} + 1\)
  • 选了一个后缀,设满足 \(a_j = i\)\(j\) 的集合的第 \(k\) 大的下标为 \(b_{i,k}\),则对于 \(j\) 满足 \(b_{i,k+1} < j \le b_{i,k}\)\(f_{i,j} = f_{i-1,j} + k\)

详见代码。复杂度高且不好写。

#include<bits/stdc++.h>
#define pii std::pair<int,int>
#define mkp std::make_pair
#define fir first
#define sec second
typedef long long ll;
inline void rd(){}
template<typename T,typename ...U>
inline void rd(T &x,U &...args){
    int ch=getchar();
    T f=1;x=0;
    while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();}
    while(ch>='0'&&ch<='9')x=(x<<1)+(x<<3)+(ch^48),ch=getchar();
    x*=f;rd(args...);
}
const int N=2e5+5,INF=1e9;
int T,n,a[N],vis[N],tmp[N];
std::vector<int> vec[N];
int mx[N<<2],tag[N<<2];
inline void PushUp(int i){mx[i]=std::max(mx[i*2],mx[i*2+1]);}
inline void Addtag(int i,int x){mx[i]+=x,tag[i]+=x;}
inline void PushDown(int i){
    if(!tag[i])return ;
    Addtag(i*2,tag[i]);Addtag(i*2+1,tag[i]);
    tag[i]=0;
}
void Build(int i,int l,int r){
    tag[i]=0;
    if(l==r)return mx[i]=-INF,void();
    int mid=(l+r)>>1;
    Build(i*2,l,mid);Build(i*2+1,mid+1,r);
    PushUp(i);
}
void Update(int i,int l,int r,int L,int R,int x){
    if(L<=l&&r<=R)return Addtag(i,x),void();
    int mid=(l+r)>>1;PushDown(i);
    if(L<=mid)Update(i*2,l,mid,L,R,x);
    if(R>mid)Update(i*2+1,mid+1,r,L,R,x);
    PushUp(i);
}
void Modify(int i,int l,int r,int x,int v){
    if(l==r)return mx[i]=std::max(mx[i],v),void();
    int mid=(l+r)>>1;PushDown(i);
    if(x<=mid)Modify(i*2,l,mid,x,v);
    else Modify(i*2+1,mid+1,r,x,v);
    PushUp(i);
}
int Query(int i,int l,int r,int L,int R){
    if(L>R)return -INF;
    if(L<=l&&r<=R)return mx[i];
    int mid=(l+r)>>1,ans=-INF;PushDown(i);
    if(L<=mid)ans=std::max(ans,Query(i*2,l,mid,L,R));
    if(R>mid)ans=std::max(ans,Query(i*2+1,mid+1,r,L,R));
    return ans;
}
inline void Solve(){
    for(int i=1;i<=n;i++)if(a[i]<=n)vis[a[i]]=0,vec[a[i]].clear();
    rd(n);
    for(int i=1;i<=n;i++){
        rd(a[i]);
        if(a[i]<=n)vis[a[i]]=1,vec[a[i]].push_back(i);
    }
    int bas=0,lim=0,sum=0,ans=0;
    for(int i=0;i<=n;i++){
        if(!vis[i]){
            lim=i;
            for(int j=1;j<=n;j++)if(a[j]>lim)++bas;
            break;
        }
    }
    for(int i=0;i<lim;i++)sum+=vec[i].size();
    Build(1,1,n);
    int now=vec[0].size();
    for(int x:vec[0])Modify(1,1,n,x,1);
    ans=std::max(ans,sum-now);
    for(int i=1;i<lim;i++){
        now+=vec[i].size();
        ans=std::max(ans,mx[1]+sum-now);
        for(int x:vec[i])tmp[x]=Query(1,1,n,x,n)+1;
        for(int j=(int)vec[i].size()-1;j>=0;j--){
            int x=vec[i][j];
            int y=j==0?1:(vec[i][j-1]+1);
            Update(1,1,n,y,x,(int)vec[i].size()-j);
        }
        for(int x:vec[i])Modify(1,1,n,x,tmp[x]);
    }
    printf("%d\n",std::max(ans+bas,mx[1]+bas));
}
signed main(){
    rd(T);
    while(T--)Solve();
    return 0;
}
posted @ 2025-02-12 20:22  KIreteria  阅读(4)  评论(0编辑  收藏  举报