CF2226E 思路分享(线段树,二分图匹配,霍尔定理)
https://codeforces.com/problemset/problem/2226/E
题意
对于序列 \([c_1,c_2,\cdots,c_m]\),定义 \(f(c)\):
-
选择序列 \([b_1,b_2,\cdots,b_m]\),满足 \(b_i \ge 1\).
-
将 \(c_i := c_i \mod b_i\).
-
\(f(c)\) 为此时的 \(mex([c_1,c_2,\cdots,c_m])\).
给定长度为 \(n\) 的序列 \(a\),对每个 \(i\),求 \(f(a[1,\cdots,i])\).
\(1\le n \le 2\cdot 10^5\).
思路
分析可知,\(a_i\) 能变成 \(j \in [0,\left\lfloor \frac{a_i-1}{2} \right\rfloor]\) 或保持不变.
对于 \(j\in [0,mex-1]\),优先选 \(a_i=j\),否则选 \(a_i \gt 2j\).
二分答案,倒序贪心,可以通过 easy version.
考虑如何判断 \(mex\) 是否可行.
对于 \(j\in [0,mex-1]\),定义没有被单点匹配(\(a_i=j\))的 \(j\) 构成的集合为 \(S\).
定义没有匹配单点的 \(a_i\) 构成的集合为 \(T\).
问题转化为 \(S\) 与 \(T\) 的匹配问题,显然 \(S\) 所有元素都必须被选中,\(t_i \in T\) 可以匹配 \(S\) 中 \([0,\left\lfloor \frac{t_i-1}{2} \right\rfloor]\) 范围内任意元素.
方便起见,令 \(t_i’ = \left\lfloor \frac{t_i-1}{2} \right\rfloor\),将 \(t_i'\) 加入 \(T\) 集 .
根据霍尔定理,在 \(S\) 集任取一个子集 \(A\),其邻集 \(N(A)\) 需满足 \(|N(A)|\ge |A|\).
因为 \(s_i\) 与所有 \(t_j \ge s_i\) 的 \(t_j\) 有边,因此 \(N(A)\) 取决于 \(A\) 中最小元素,记为 \(s_p\).
在最小元素固定的基础上,\(|A|\) 最坏为:\(S\) 中大于等于 \(s_p\) 的数量;\(|N(A)|\) 恒定,为 \(T\) 中大于等于 \(s_p\) 的数量.
记 \(A_i\) 为 \(S\) 中大于等于 \(i\) 的元素数量,\(B_i\) 为 \(T\) 中大于等于 \(i\) 的元素数量,考虑用线段树维护 \(val = B_i - A_i\) 的最小值.
当 \(i \in [0,mex-1]\) 且 \(i \notin S\) 时,仍然成立,因此 \(mex\) 可行的条件是
考虑增量维护,\(vis\) 记录 \(i \lt mex\) 是否已被单点匹配,\(cnt\) 维护 \(T\) 集中原来元素为 \(t_i\) 的数量.
将 \(s_i\) 加入 \(S\) 集相当于对 \([0,s_i]\) 区间减 \(1\),将 \(t_i'\) 加入 \(T\) 集相当于对 \([0,\left\lfloor \frac{t_i-1}{2} \right\rfloor]\) 区间加 \(1\).
新增元素 \(a_i\).
-
若 \(a_i \lt mex\) 且 \(a_i\) 未被单点匹配,说明 \(a_i\) 已被加入 \(S\) 集中,将 \(a_i\) 从 \(S\) 集中删除,标记 \(a_i\) 为已被单点匹配.
-
否则将 \(a_i'\) 加入 \(T\) 集.
然后考虑扩大 \(mex\).
-
若 \(cnt_{mex} \gt 0\),将 \(T\) 集中的一个 \(t_i = mex\) 拿出做单点匹配,删掉 \(t_i'\).
-
否则将 \(mex\) 加入 \(S\) 集.
操作后,判断 \(mex\) 是否可行,不可行回退修改.
时间复杂度 \(\mathcal{O}(n\log n)\).
代码
//author:kzssCCC
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const ll INF = 9e18;
class segmentTree{
public:
class node{
public:
ll minn = INF;
ll lazy = 0;
};
int n;
vector<node> seg;
segmentTree(int _n){
n = _n;
seg = vector<node>(4*n+1);
}
node merge(node p1,node p2){
node temp;
temp.minn = min(p1.minn,p2.minn);
return temp;
}
void build(vector<ll>& a){
build(1,1,n,a);
}
void build(int rt,int l,int r,vector<ll>& a){
if (l==r){
seg[rt].minn = a[l];
return;
}
int mid = l+r >> 1;
build(rt<<1,l,mid,a);
build(rt<<1|1,mid+1,r,a);
seg[rt] = merge(seg[rt<<1],seg[rt<<1|1]);
}
void push_down(int rt,int l,int r){
if (seg[rt].lazy==0) return;
seg[rt<<1].minn += seg[rt].lazy;
seg[rt<<1].lazy += seg[rt].lazy;
seg[rt<<1|1].minn += seg[rt].lazy;
seg[rt<<1|1].lazy += seg[rt].lazy;
seg[rt].lazy = 0;
}
void update(int pos,ll val){
if (pos<1 || pos>n) return;
update(1,1,n,pos,val);
}
void update(int rt,int l,int r,int pos,ll val){
if (l==r){
//
return;
}
int mid = l+r >> 1;
push_down(rt,l,r);
if (pos<=mid){
update(rt<<1,l,mid,pos,val);
}
else{
update(rt<<1|1,mid+1,r,pos,val);
}
seg[rt] = merge(seg[rt<<1],seg[rt<<1|1]);
}
void update_range(int x,int y,ll val){
if (x>y || x<1 || x>n || y<1 || y>n) return;
update_range(1,1,n,x,y,val);
}
void update_range(int rt,int l,int r,int x,int y,ll val){
if (r<x || l>y){
return;
}
if (x<=l && y>=r){
seg[rt].minn += val;
seg[rt].lazy += val;
return;
}
int mid = l+r >> 1;
push_down(rt,l,r);
update_range(rt<<1,l,mid,x,y,val);
update_range(rt<<1|1,mid+1,r,x,y,val);
seg[rt] = merge(seg[rt<<1],seg[rt<<1|1]);
}
node query(int pos){
if (pos<1 || pos>n) return {};
return query(1,1,n,pos);
}
node query(int rt,int l,int r,int pos){
if (l==r){
return seg[rt];
}
int mid = l+r >> 1;
push_down(rt,l,r);
if (pos<=mid){
return query(rt<<1,l,mid,pos);
}
else{
return query(rt<<1|1,mid+1,r,pos);
}
}
node query_range(int l,int r){
if (l<1 || l>n || r<1 || r>n || l>r) return {};
return query_range(1,1,n,l,r);
}
node query_range(int rt,int l,int r,int x,int y){
if (r<x || l>y){
return {};
}
if (x<=l && y>=r){
return seg[rt];
}
int mid = l+r >> 1;
push_down(rt,l,r);
return merge(query_range(rt<<1,l,mid,x,y),query_range(rt<<1|1,mid+1,r,x,y));
}
bool is_valid(int rt,int l,int r,int x,int y,ll val){
if (r<x || l>y) return false;
//
return false;
}
int first_valid(int l,int r,ll val){
if (l>r || l<1 || l>n || r<1 || r>n) return -1;
return first_valid(1,1,n,l,r,val);
}
int first_valid(int rt,int l,int r,int x,int y,ll val){
if (!is_valid(rt,l,r,x,y,val)) return -1;
if (l==r){
return l;
}
int mid = l+r >> 1;
push_down(rt,l,r);
int res = first_valid(rt<<1,l,mid,x,y,val);
if (res!=-1){
return res;
}
return first_valid(rt<<1|1,mid+1,r,x,y,val);
}
int last_valid(int l,int r,ll val){
if (l>r || l<1 || l>n || r<1 || r>n) return -1;
return last_valid(1,1,n,l,r,val);
}
int last_valid(int rt,int l,int r,int x,int y,ll val){
if (!is_valid(rt,l,r,x,y,val)) return -1;
if (l==r){
return l;
}
int mid = l+r >> 1;
push_down(rt,l,r);
int res = last_valid(rt<<1|1,mid+1,r,x,y,val);
if (res!=-1){
return res;
}
return last_valid(rt<<1,l,mid,x,y,val);
}
};
void solve(){
int n;
cin >> n;
vector<int> a(n+1);
for (int i=1;i<=n;i++){
cin >> a[i];
}
int V = *max_element(a.begin()+1,a.end());
vector<int> cnt(max(V,n)+10);
vector<bool> vis(max(V,n)+10,false);
segmentTree sg(max(V,n)+9);
{
vector<ll> temp(max(V,n)+10);
sg.build(temp);
}
vector<int> res(n+1);
int mex = 0;
for (int i=1;i<=n;i++){
if (!vis[a[i]] && a[i]<mex){
vis[a[i]] = true;
sg.update_range(1,a[i]+1,1);
}
else{
cnt[a[i]]++;
if (a[i]>0) sg.update_range(1,(a[i]-1)/2+1,1);
}
while (1){
if (cnt[mex]>0){
cnt[mex]--;
if (mex>0){
sg.update_range(1,(mex-1)/2+1,-1);
}
if (sg.query_range(1,mex+1).minn>=0){
vis[mex] = true;
mex++;
}
else{
cnt[mex]++;
if (mex>0){
sg.update_range(1,(mex-1)/2+1,1);
}
break;
}
}
else{
sg.update_range(1,mex+1,-1);
if (sg.query_range(1,mex+1).minn>=0){
mex++;
}
else{
sg.update_range(1,mex+1,1);
break;
}
}
}
res[i] = mex;
}
for (int i=1;i<=n;i++){
cout << res[i] << ' ';
}
cout << '\n';
}
int main(){
ios::sync_with_stdio(false);
cin.tie(0);
int t = 1;
cin >> t;
while (t--) solve();
return 0;
}

浙公网安备 33010602011771号