LOJ6062 Pair

Pair

给出一个长度为 $ n $ 的数列 $ { a_i } $ 和一个长度为 $ m $ 的数列 $ { b_i } $,求 $ { a_i } $ 有多少个长度为 $ m $ 的连续子数列能与 $ { b_i } $ 匹配。

两个数列可以匹配,当且仅当存在一种方案,使两个数列中的数可以两两配对,两个数可以配对当且仅当它们的和不小于 $ h $。

对于 $ 100% $ 的数据,$ 1 \leq m \leq n \leq 150000 $;

Hall定理

对于二分图中的集合 X和 Y(|X|≤|Y|),任取一个 X的子集 s(s⊆X),设 Y中与 s有边相连的点集为 N(s),若 X和 Y存在完美匹配(完美匹配指的是匹配数 =|X|),则一定满足 |s|≤|N(s)|

反之亦然,即这是该图存在完美匹配的充要条件。

Hall定理在题目中的运用主要是为了减少判断量(不然跑网络流即可)。要想用好Hall定理,必须得发掘具体题目的性质。

题解

https://www.mina.moe/archives/11412

由于a,b是独立的,因此可以看成二分图

由于b的顺序与答案无关,因此可以先把b从小到大排个序

设当前枚举的连续的一段a的子数列为集合X
对于bi来说,X中与b1,b2,b3,…,bi–1相邻的点都一定与bi相邻

因此在b中选择一个子集k,若bi是k中最大元素,那么|N(k)|就等于X中与bi相邻的点数,且|k|的最大取值就是i,需要满足|N(k)|≥|k|

也就是说要求X中与bi相邻的点数要≥i,所以我们要维护X中与bi相邻的点数。

注意到对于每个ai,与其相邻的点集是b的一个连续后缀,因此可以用线段树维护。

时间复杂度 \(O(n\log n)\)

CO int N=15e4+10;
int A[N],B[N];
int val[4*N],tag[4*N];

#define lc (x<<1)
#define rc (x<<1|1)
#define mid ((l+r)>>1)
IN void push_up(int x){
	val[x]=min(val[lc],val[rc]);
}
IN void push_down(int x){
	if(tag[x]){
		val[lc]+=tag[x],tag[lc]+=tag[x];
		val[rc]+=tag[x],tag[rc]+=tag[x];
		tag[x]=0;
	}
}
void build(int x,int l,int r){
	if(l==r) {val[x]=-l; return;}
	build(lc,l,mid),build(rc,mid+1,r);
	push_up(x);
}
void modify(int x,int l,int r,int ql,int qr,int v){
	if(ql<=l and r<=qr) {val[x]+=v,tag[x]+=v; return;}
	push_down(x);
	if(ql<=mid) modify(lc,l,mid,ql,qr,v);
	if(qr>mid) modify(rc,mid+1,r,ql,qr,v);
	push_up(x);
}
#undef lc
#undef rc
#undef mid

int main(){
	int n=read<int>(),m=read<int>(),H=read<int>();
	for(int i=1;i<=m;++i) read(B[i]);
	for(int i=1;i<=n;++i) read(A[i]);
	sort(B+1,B+m+1);
	build(1,1,m);
	for(int i=1;i<m;++i){
		int j=lower_bound(B+1,B+m+1,H-A[i])-B;
		if(j<=m) modify(1,1,m,j,m,1);
	}
	int ans=0;
	for(int i=m;i<=n;++i){
		int j=lower_bound(B+1,B+m+1,H-A[i])-B;
		if(j<=m) modify(1,1,m,j,m,1);
		ans+=val[1]>=0;
		j=lower_bound(B+1,B+m+1,H-A[i-m+1])-B;
		if(j<=m) modify(1,1,m,j,m,-1);
	}
	printf("%d\n",ans);
	return 0;
}

posted on 2020-04-09 20:50  autoint  阅读(217)  评论(0编辑  收藏  举报

导航