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\) 可行的条件是

\[\min(val_0,val_1,\cdots,val_{mex-1}) \ge 0 \]

考虑增量维护,\(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;
}
posted @ 2026-06-28 20:23  kzssCCC  阅读(4)  评论(0)    收藏  举报