Codeforces 338E - Optimize!(Hall 定理+线段树)

题面传送门

首先 \(b_i\) 的顺序肯定不会影响匹配,故我们可以直接将 \(b\) 数组从小到大排个序。

我们考虑分析一下什么样的长度为 \(m\) 的数组 \(a_1,a_2,\dots,a_m\) 能和 \(b\) 数组形成匹配。考虑对于 \(i,j\in [1,m]\),若 \(a_i+b_j\geq h\),就在 \(i,j\) 之间连边,那么形成的图必然是一张二分图,我们只需检验这张二分图是否存在完美匹配即可。

这时候就要用到一个叫做 Hall 定理的科技了。Hall 定理说的是这样一件事,对于二分图 \(G=(V_1,V_2,E)\),定义函数 \(f(V)(V\in V_1)\) 为与点集 \(V\) 中的点相连的点的(右部点)集合。那么二部图 \(G\) 有完美匹配的充要条件是 \(\forall V\subseteq V_1,|f(V)|\geq |V|\)

必要性:这个就比较显然了吧。。。记对于节点 \(u\),记 \(mch(u)\) 为与 \(u\) 匹配的节点。那么我们构造集合 \(V'=\{v|v=mch(u),u\in V\}\),那么 \(|V'|=|V|\),而根据 \(V'\) 的构造方式可知 \(\forall v\in V'\) 至少存在一个 \(u\in V\) 满足 \(u,v\) 间有边,故 \(V'\subseteq f(V)\),于是有 \(|V|=|V'|\leq f(V)\),得证。

充分性:这个就没那么显然了。考虑反证法,假设二分图 \(G\) 不存在完美匹配但满足 Hall 定理。那么我们构造出 \(G\) 的一种最大匹配,其中必存在某个非匹配点,假设其为 \(A\)。根据 Hall 定理 \(A\) 必定与另一边某个点相连,设其为 \(B\)。而 \(B\) 必须为匹配点,否则 \(A,B\) 就能形成新的匹配,不满足最大匹配的条件了,设 \(C\) 为与 \(B\) 相匹配的点。再对集合 \(\{A,C\}\) 使用 Hall 定理可知,\(\{A,C\}\)\(B\) 外必与其它某个点相连,设其为 \(D\)\(D\) 也必须为匹配点,否则根据之前的证明过程可知它不能与 \(A\) 相连,否则 \(A,D\) 能形成新的匹配,故它只能与 \(C\) 相连,而若它与 \(C\) 相连,那么将匹配边 \((B,C)\) 换为 \((A,B),(C,D)\) 可让匹配个数多 \(1\),不满足最大匹配的条件,故 \(D\) 一定与某个点 \(E\) 匹配。再对集合 \(\{A,C,E\}\) 使用 Hall 定理可得还存在某个点 \(F\) 与这三个点都相连且为匹配点。如此一直进行下去可进行无限轮,而点集的大小是有限的,矛盾!

回到本题来,设 \(c(j)\) 为排好序后与 \(b_j\) 能匹配的 \(a_i\) 的个数。由于我们 \(b\) 数组是有序的,那么就有 \(\forall i<j,c(i)<c(j)\),也就是说对于 \(i<j\),所有能与 \(b_i\) 匹配的 \(a_k\) 都能和 \(b_j\) 匹配。而根据 Hall 定理,原图存在完美匹配的充要条件是 \(\forall V\subseteq V_1,|f(V)|\geq |V|\),故对于大小为 \(k\) 的集合 \(S=\{b_{i_1},b_{i_2},\dots,b_{i_s}\}\) 必有 \(\max(f(i_1),f(i_2),\dots,f_(i_s))\geq k\),而显然所有这样的集合中 \(\max(f(i_1),f(i_2),\dots,f_(i_s))\) 的最小值为 \(f(k)\)。故连边形成的图存在完美匹配等价于 \(\forall i,f(i)\geq i\)

最后考虑加上 \(a\) 数组之后怎样处理,考虑建一棵线段树维护 \(f(i)\)。显然对于每个 \(a_i\) 可二分找出最小的满足 \(b_j\geq h-a_i\)\(j\) 并在线段树 \([j,m]\) 的位置整体加 \(1\)。那么怎么知道 \(\forall i,f(i)\geq i\) 是否成立呢?我们就初始在线段树上每个位置赋上 \(-i\) 并检查全局最小值是否 \(\geq 0\) 就行了。

时间复杂度线对。

#include <bits/stdc++.h>
using namespace std;
#define fi first
#define se second
#define fz(i,a,b) for(int i=a;i<=b;i++)
#define fd(i,a,b) for(int i=a;i>=b;i--)
#define ffe(it,v) for(__typeof(v.begin()) it=v.begin();it!=v.end();it++)
#define fill0(a) memset(a,0,sizeof(a))
#define fill1(a) memset(a,-1,sizeof(a))
#define fillbig(a) memset(a,63,sizeof(a))
#define pb push_back
#define ppb pop_back
#define mp make_pair
template<typename T1,typename T2> void chkmin(T1 &x,T2 y){if(x>y) x=y;}
template<typename T1,typename T2> void chkmax(T1 &x,T2 y){if(x<y) x=y;}
typedef pair<int,int> pii;
typedef long long ll;
template<typename T> void read(T &x){
	x=0;char c=getchar();T neg=1;
	while(!isdigit(c)){if(c=='-') neg=-1;c=getchar();}
	while(isdigit(c)) x=x*10+c-'0',c=getchar();
	x*=neg;
}
const int MAXN=1.5e5;
const int INF=0x3f3f3f3f;
int n,m,k,a[MAXN+5],b[MAXN+5],pos[MAXN+5];
struct node{int l,r,mn,lz;} s[MAXN*4+5];
void build(int k,int l,int r){
	s[k].l=l;s[k].r=r;if(l==r){s[k].mn=-l;return;}
	int mid=(l+r)>>1;build(k<<1,l,mid);build(k<<1|1,mid+1,r);
	s[k].mn=min(s[k<<1].mn,s[k<<1|1].mn);
}
void pushdown(int k){
	if(s[k].lz!=0){
		s[k<<1].mn+=s[k].lz;s[k<<1].lz+=s[k].lz;
		s[k<<1|1].mn+=s[k].lz;s[k<<1|1].lz+=s[k].lz;
		s[k].lz=0;
	}
}
void modify(int k,int l,int r,int x){
	if(l>r) return;
	if(l<=s[k].l&&s[k].r<=r){s[k].mn+=x;s[k].lz+=x;return;}
	pushdown(k);int mid=(s[k].l+s[k].r)>>1;
	if(r<=mid) modify(k<<1,l,r,x);
	else if(l>mid) modify(k<<1|1,l,r,x);
	else modify(k<<1,l,mid,x),modify(k<<1|1,mid+1,r,x);
	s[k].mn=min(s[k<<1].mn,s[k<<1|1].mn);
}
int main(){
	scanf("%d%d%d",&n,&m,&k);
	for(int i=1;i<=m;i++) scanf("%d",&a[i]);
	a[0]=-INF;sort(a+1,a+m+1);build(1,1,m);
	for(int i=1;i<=n;i++){
		scanf("%d",&b[i]);
		pos[i]=lower_bound(a,a+m+1,k-b[i])-a;
//		printf("%d\n",pos[i]);
	}
	for(int i=1;i<m;i++) modify(1,pos[i],m,1);
	int ans=0;
	for(int i=m;i<=n;i++){
		modify(1,pos[i],m,1);
//		printf("%d\n",s[1].mn);
		if(s[1].mn>=0) ans++;
		modify(1,pos[i-m+1],m,-1);
	} printf("%d\n",ans);
	return 0;
}
posted @ 2021-02-16 23:44  tzc_wk  阅读(178)  评论(0编辑  收藏  举报