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;
}