【LOJ】「2017 山东一轮集训 Day2」Pair-霍尔定理

传送门:LibreOJ-6062Pair


题意

给出一个长度为 n n 的数列 {ai} { a i } 和一个长度为 m m 的数列 {bi} { b i } ,求 {ai} { a i } 有多少个长度为 m m 的连续子数列能与 {bi} { b i } 匹配。
两个数列可以匹配,当且仅当存在一种方案,使两个数列中的数可以两两配对,两个数可以配对当且仅当它们的和不小于 h h


数据范围

对于 10% 10\% 10% 的数据, 1mn10 1 ≤ m ≤ n ≤ 10
对于 40% 40\% 40% 的数据, 1mn1000 1 ≤ m ≤ n ≤ 1000
对于 100% 100\% 100% 的数据, 1mn150000 1 ≤ m ≤ n ≤ 150000
对于 100% 100\% 100% 的数据, 1ai,bi,h109 1 ≤ a i , b i , h ≤ 10 9 ​​ 。


题解

霍尔定理(皮一皮,自己上网搜)一秒出题解。
我们可以先对b按升序排序。所以每个 ai a i 满足的b的区间都是b的一个后缀,不然就没有。
然后我们每次加入一个数时,把符合条件(即后缀)中的所有b上的值+1,(姑且设一个数组为 sum[i] s u m [ i ] )然后判断一下对于每个 bi b i ,是否都满足 sum[i]i s u m [ i ] ≤ i (由霍尔定理)。
然而本蒟蒻始终有一种不详的预感:然而我们还是要扫一遍,一个一个加入线段树中又一个一个删去。这样写会T。
果真本蒟蒻只拿了40分。
然而我们维护的并不是 sum s u m ,而是每个位置上的 sum[i]i s u m [ i ] − i 。每次 O(1) O ( 1 ) 判断最小的这个值是否大于0就好了。
orzzzz


代码

#include
#include
#include
#include
#define IT int
#define rg register
using namespace std;
const IT N=150050;

IT n,m,b[N],H,in[N];
IT ans,add[N<<2],mn[N<<2];

char nc()
{
    static char buf[100000],*L=buf,*R=buf;
    if(L==R){R=(L=buf)+fread(buf,1,1e5,stdin);}
    if(L==R)return EOF;else return *L++;
}

inline int read()
{
    int num=0;char ch=nc();
    while(ch<'0' || ch>'9') ch=nc();
    while(ch>='0' && ch<='9') {num=(num<<3)+(num<<1)+(ch^48);ch=nc();}
    return num;
}

inline IT min(IT x,IT y)
{
    return x>y? y:x;
}

inline void pushup(IT k)
{
   mn[k]=min(mn[k<<1],mn[k<<1|1]);
}

inline void pushdown(IT k,IT l,IT r)
{
    if(add[k]!=0){
        IT mid=(l+r)>>1;
        add[k<<1]+=add[k];
        mn[k<<1]+=add[k];
        add[k<<1|1]+=add[k];
        mn[k<<1|1]+=add[k];
        add[k]=0;
    }
}

inline IT find(IT x)
{
    IT l=1,r=m;
    while(l<=r){
        IT mid=(l+r)>>1;
        if(b[mid]>=x) r=mid-1;
        else l=mid+1;
    }
    return l;
}

inline void build(IT k,IT l,IT r)
{
    if(l==r){
        mn[k]=-l;return;
    }
    IT mid=(l+r)>>1;
    build(k<<1,l,mid);
    build(k<<1|1,mid+1,r);
    pushup(k);
}

inline void ad(IT k,IT l,IT r,IT L,IT val)
{
    pushdown(k,l,r);
    if(l>=L){
        add[k]+=val;
        mn[k]+=val;
        return;
    }
    int mid=(l+r)>>1;
    if(L<=mid) ad(k<<1,l,mid,L,val);
    ad(k<<1|1,mid+1,r,L,val);
    pushup(k);
}

inline int get(IT k,IT l,IT r)
{
    pushdown(k,l,r);
    return mn[k];
}

int main(){
    n=read();m=read();H=read();
    for(rg IT i=1;i<=m;i++){
        b[i]=read();
    }
    sort(b+1,b+m+1);
    for(rg IT i=1;i<=n;i++){
        in[i]=find(H-read());
    }
    build(1,1,m);
    for(rg IT i=1;i<=m;i++){
        if(in[i]>m) continue;
        ad(1,1,m,in[i],1); 
    }
    if(get(1,1,m)>=0) ans++;
    for(rg int i=m+1;i<=n;i++){
        if(in[i-m]<=m)
        ad(1,1,m,in[i-m],-1);
        if(in[i]<=m)
        ad(1,1,m,in[i],1);
        if(get(1,1,m)>=0) ans++;
    }
    printf("%d\n",ans);
    return 0;
}

ps:请无视掉本蒟蒻开始为了卡常(然而还是T了)加的ncbuf

你可能感兴趣的:(---组合数学---,霍尔定理)