cf 338E
题目大意
有长度为 m 的序列 b 和长度为 n 的序列 a,问序列 a 中有多少个长度为 m 的区间能和序列 b 匹配。两个序列中的元素 x; y 能配对当且仅当 x + y ≥ h,两个序列能匹配当且仅当存在完备匹配。
题解
非常简单的线段树题。做变换b[i]=h-b[i],再排个序,就转换成了这样的问题,每次考虑a中连续的m个数,要求这些数中比b数组中第i个数b[i]大的有至少m-i+1个。这个线段树维护一下就可以了。
#include <bits/stdc++.h> #define ll long long #define inf 0x6fffffff #define N 150086 using namespace std; int read(){ int x=0,f=1;char ch=getchar(); while(ch<'0'||ch>'9'){if(ch=='-')f=-1;ch=getchar();} while(ch>='0'&&ch<='9'){x=x*10+ch-'0';ch=getchar();} return x*f; } int n,m,h,ans; int a[N],b[N]; map<int,int>mp; int cnt,tt; int f[N*4],tag[N*4]; int tmp[2*N]; void build(int i,int l,int r,int ps,int dd) { if(l==r){f[i]=-dd;return; } int mid=(l+r)/2; if(ps<=mid)build(i*2,l,mid,ps,dd); else build(i*2+1,mid+1,r,ps,dd); f[i]=min(f[i*2],f[i*2+1]); } void ins(int i,int l,int r,int lt,int rt,int dd) { if(lt<=l&&r<=rt){tag[i]+=dd;f[i]+=dd;return;} int mid=(l+r)/2; if(lt<=mid)ins(i*2,l,mid,lt,rt,dd); if(mid+1<=rt)ins(i*2+1,mid+1,r,lt,rt,dd); f[i]=min(f[i*2],f[i*2+1])+tag[i]; } int main() { int n=read(),m=read(),h=read(); for(int i=1;i<=m;i++)b[i]=h-read(); for(int i=1;i<=n;i++)a[i]=read(); for(int i=1;i<=n;i++)tmp[++tt]=a[i]; for(int i=1;i<=m;i++)tmp[++tt]=b[i]; sort(tmp+1,tmp+tt+1);sort(b+1,b+m+1); for(int i=1;i<=tt;i++)if(mp[tmp[i]]==0)mp[tmp[i]]=++cnt; for(int i=1;i<=n;i++)a[i]=mp[a[i]]; for(int i=1;i<=m;i++)b[i]=mp[b[i]]; //for(int i=1;i<=n;i++)printf("%d%c",a[i],(i!=n)?' ':'\n'); //for(int i=1;i<=m;i++)printf("%d%c",b[i],(i!=m)?' ':'\n'); for(int i=1;i<=m;i++)build(1,1,cnt,b[i],m-i+1); for(int i=1;i<=m;i++)ins(1,1,cnt,1,a[i],1); if(f[1]>=0)ans=1;else ans=0; for(int i=m+1;i<=n;i++) { ins(1,1,cnt,1,a[i],1); ins(1,1,cnt,1,a[i-m],-1); if(f[1]>=0)ans++; } cout<<ans<<endl; //system("pause"); return 0; }