CF1870G MEXanization
思路
发现除第一个外,答案是递增的。考虑如何 check 答案是否大于 \(z\)。
统计除每个数出现的次数 \(g\) 对于每个 \(z\) 把所有大于或等于 \(z\) 的数不会有贡献,所以把它们全变成 \(0\)。从 \(z-1\) 开始向前枚举,设目前枚举到 \(i\),\([0,i]\) 中的每个数需要\(need\) 个。
如果 \(need\geq g_i\) 那么 \([0,i-1]\) 中的每个数又额外需要 \(need-g_i\) 取部 \(i\) 缺少的部分。所以 \(need \leftarrow need+need-g_i\)。
如果 \(need\leq g_i\) 那么 \(i\) 就多出了 \(g_i-need\) 个可以补到 \(0\),显然补过去一定是不劣的。
\([1,z-1]\) 枚举完之后判断一下 \(need\) 和 \(g_0\) 加所有补过来的数的大小关系即可。
考虑优化。由于 \(need>n\) 时一定不合法,而 \(need\) 的增长是非常快的,在 \(need>n\) 之前满足 \(need\geq g_i\) 的位置个数是 \(\mathcal{O}(\sqrt{n})\) ,所以可以用线段树优化,时间复杂度 \(\mathcal{O}(n\sqrt{n})\)。
实现
设目前要 check 的答案为 \(ans\) 开一颗线段树维护每个数字出现的次数。值大于或等于 \(ans\) 的数没有贡献,把他们全变成零。按照先右儿子在左儿子的顺序便利线段树。
如果当前区间与区间 \([1,ans-1]\) 没有交集就返回。
如果当前区间被区间 \([1,ans-1]\) 包含并且最小值大于或等于 \(need\) 就把多出来的全部补到 \(0\) 上。
否则继续遍历。当遍历到叶子节点时更新 \(need\)。
代码
#include<bits/stdc++.h>
using namespace std;
namespace IO{
template<typename T>
inline void read(T&x){
x=0;char c=getchar();bool f=0;
while(!isdigit(c)) c=='-'?f=1:0,c=getchar();
while(isdigit(c)) x=x*10+c-'0',c=getchar();
f?x=-x:0;
}
template<typename T>
inline void write(T x){
if(x==0){putchar('0');return ;}
x<0?x=-x,putchar('-'):0;short st[50],top=0;
while(x) st[++top]=x%10,x/=10;
while(top) putchar(st[top--]+'0');
}
inline void read(char&c){c=getchar();while(isspace(c)) c=getchar();}
inline void write(char c){putchar(c);}
inline void read(string&s){s.clear();char c;read(c);while(!isspace(c)&&~c) s+=c,c=getchar();}
inline void write(string s){for(int i=0,len=s.size();i<len;i++) putchar(s[i]);}
template<typename T>inline void write(T*x){while(*x) putchar(*(x++));}
template<typename T,typename...T2> inline void read(T&x,T2&...y){read(x),read(y...);}
template<typename T,typename...T2> inline void write(const T x,const T2...y){write(x),putchar(' '),write(y...),sizeof...(y)==1?putchar('\n'):0;}
}using namespace IO;
const int maxn=400010;
int n,a[maxn],g[maxn],maxx,r[maxn];
class Segment_Tree{
private:
int gs,need;bool flag;
struct node{int minn,h;}t[maxn*8];
void update(int u,int l,int r,int d,int z){
if(l>d||r<d) return ;
if(l==r){t[u].minn=t[u].h=z;return ;}
int mid=l+r>>1;
update(u<<1,l,mid,d,z);update(u<<1|1,mid+1,r,d,z);
t[u].minn=min(t[u<<1].minn,t[u<<1|1].minn);
t[u].h=t[u<<1].h+t[u<<1|1].h;
}
int find(int u,int l,int r,int ll,int rr,int z){
if(l>rr||r<ll) return -1;
if(t[u].minn>=z) return -1;
if(l==r) return l;
int mid=l+r>>1;
int zz=find(u<<1|1,mid+1,r,ll,rr,z);
if(zz==-1) return find(u<<1,l,mid,ll,rr,z);
return zz;
}
int query(int u,int l,int r,int ll,int rr){
if(l>rr||r<ll) return 0;
if(ll<=l&&r<=rr) return t[u].h;
int mid=l+r>>1;
return query(u<<1,l,mid,ll,rr)+query(u<<1|1,mid+1,r,ll,rr);
}
void js(int u,int l,int r,int ll,int rr){
if(flag) return ;
if(l>rr||r<ll) return ;
if(ll<=l&&r<=rr) if(t[u].minn>=need){gs+=t[u].h-(r-l+1)*need;return ;}
if(l==r){
need+=need-t[u].minn;
if(need>n) flag=1;
return ;
}
int mid=l+r>>1;
js(u<<1|1,mid+1,r,ll,rr);
js(u<<1,l,mid,ll,rr);
}
public:
void update(int d,int z){update(1,0,n,d,z);}
bool check(int l,int r){need=1,gs=::r[0];flag=0;js(1,0,n,l,r);return need<=gs;}
void clear(int u=1,int l=0,int r=n){
t[u].minn=t[u].h=0;
if(l==r) return ;
int mid=l+r>>1;
clear(u<<1,l,mid),clear(u<<1|1,mid+1,r);
}
}t;
bool check(int z){return t.check(1,z-1);}
void solve(){
t.clear();
read(n);
for(int i=1;i<=n;i++) read(a[i]);
int ans=0;
if(a[1]==0) ans=1;
g[a[1]]++;if(a[1]>ans) r[0]++,t.update(0,1);else r[a[1]]++,t.update(a[1],1);
write(a[1]==0?1:a[1]),write(' ');
for(int i=2;i<=n;i++){
g[a[i]]++;
if(a[i]>=ans) r[0]++,t.update(0,r[0]);else r[a[i]]++,t.update(a[i],r[a[i]]);
while(1){
int dt=g[ans];
r[0]-=dt;
t.update(0,r[0]);
r[ans]+=dt;
t.update(ans,r[ans]);
if(check(ans+1)) ans++;
else{
r[0]+=dt;
t.update(0,r[0]);
r[ans]-=dt;
t.update(ans,r[ans]);
break;
}
}
write(ans),write(' ');
}
for(int i=0;i<=n;i++) r[a[i]]=g[a[i]]=0;
write('\n');
}
signed main(){
int T;read(T);
while(T--) solve();
return 0;
}

浙公网安备 33010602011771号