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

 

posted @ 2019-03-13 17:58 OldJang 阅读(...) 评论(...) 编辑 收藏