题目大意:
给出一个长度为
的数列
和一个长度为
的数列
求
有多少个长度为
的连续子数列能与
匹配。
两个数列可以匹配,当且仅当存在一种方案,使两个数列中的数可以两两配对,两个数可以配对当且仅当它们的和不小于
。
思路:
感觉还是很好的一道题目。我是为了学Hall定理才找到这道题去做的。
首先我们来考虑如何判断两个串是否可以形成完美匹配,根据Hall定理可以得到,只有
的每一个子集和
相连的点数都大于这个子集本身的点数才具有完美匹配。显然枚举每一个子集是不现实的,但是我们发现
中和
相邻的点只取决于
中权值最大的点,设以
为最大权值的子集为
.,我们只需要判断
是否大于等于0即可。
但是这样还是不够好,把
从小到大排序之后,每一个
对应的
就是一段后缀,相当于覆盖了一条线段,满足上面的条件其实就是每一个
被覆盖的次数满足大于等于
,这样处理之后就很好用线段树来维护了。
/*===============================
* Author : ylsoi
* Problem : loj6062
* Algorithm : Hall&Segment_Tree
* Time : 2018.6.29
* ===========================*/
#include<bits/stdc++.h>
#define REP(i,a,b) for(int i=a;i<=b;++i)
typedef long long ll;
using namespace std;
void File(){
freopen("loj6062.in","r",stdin);
freopen("loj6062.out","w",stdout);
}
const int maxn=1.5e5+10;
int n,m,h,a[maxn],b[maxn],ans;
struct Segment_Tree{
#define mid ((l+r)>>1)
#define lc rt<<1
#define rc rt<<1|1
#define lson lc,l,mid
#define rson rc,mid+1,r
int Min[maxn<<2],tag[maxn<<2];
void pushdown(int rt){
Min[lc]+=tag[rt]; tag[lc]+=tag[rt];
Min[rc]+=tag[rt]; tag[rc]+=tag[rt];
tag[rt]=0;
}
void build(int rt,int l,int r){
if(l==r)Min[rt]=-l;
else{
build(lson); build(rson);
Min[rt]=min(Min[lc],Min[rc]);
}
}
void modify(int rt,int l,int r,int L,int R,int x){
if(L>R)return;
if(L<=l && r<=R){Min[rt]+=x; tag[rt]+=x;}
else{
if(tag[rt])pushdown(rt);
if(L<=mid)modify(lson,L,R,x);
if(R>=mid+1)modify(rson,L,R,x);
Min[rt]=min(Min[lc],Min[rc]);
}
}
}T;
void init(){
scanf("%d%d%d",&n,&m,&h);
REP(i,1,m)scanf("%d",&b[i]);
sort(b+1,b+m+1);
REP(i,1,n){
scanf("%d",&a[i]);
a[i]=lower_bound(b+1,b+m+1,h-a[i])-b;
}
T.build(1,1,m);
REP(i,1,m-1)T.modify(1,1,m,a[i],m,1);
}
int main(){
File();
init();
REP(i,m,n){
T.modify(1,1,m,a[i],m,1);
ans+=(T.Min[1]>=0);
T.modify(1,1,m,a[i-m+1],m,-1);
}
printf("%d\n",ans);
return 0;
}