[霍尔定理]「2017 山东一轮集训 Day2」LOJ 6062——PAIR

来源:互联网 发布:js防水怎么样 编辑:程序博客网 时间:2024/06/05 08:02

什么是霍尔定理

这是关于二分图的一个非常重要的定理。
但是博主今天第一接触,真不愧是蒟蒻。
主要内容:
M(U1)为与U1中的点相连的点集,一个二分图U,V(|U|<=|V|)存在完美匹配,满足对于任意点集xU都有|M(X)|>=|X|
感谢法老学长的PPT。

题目梗概

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

解题思路

对于这道题B数组的顺序对题目的答案没有影响,所以我们先排序方便思考。
假设现在是由小到大排。
这样子我们可以发现与Ai匹配的所有Bi所构成的集合是排序后B数组的一个后缀。
再根据霍尔定理我们马上就可推出如果Bi被至少i条边(边都属于我们枚举的一个长度为M的区间里的每个数所可以匹配的后缀)覆盖的话,这个答案就是可行的。
这样的话我们可以用一棵线段树来维护min(Xii),xi即是覆盖Bi的边数。
如果min(Xii)>=0就代表可行。

#include<cstdio>#include<algorithm>using namespace std;const int maxn=150005;struct jz{    int L,R,x,tag;}t[maxn*4];char nc(){    static char buf[100000],*L=buf,*R=buf;    if (L==R) R=(L=buf)+fread(buf,1,100000,stdin);    if (L==R) return EOF;else return *L++;}int _read(){    int num=0;char ch=nc();    while (ch<'0'||ch>'9') ch=nc();    while (ch>='0'&&ch<='9') num=num*10+ch-48,ch=nc();    return num;}int n,m,h,b[maxn],a[maxn],ans;int min(int x,int y){if (x<y) return x;else return y;} int find(int x){    int L=1,R=m,mid;    while (L<=R){        mid=L+(R-L>>1);        if (x>b[mid]) L=mid+1;else R=mid-1;     }    return L;}void Build(int x,int L,int R){    t[x].L=L;t[x].R=R;    if (L==R){t[x].x=-L;return;}    int mid=L+(R-L>>1);    Build(2*x,L,mid);Build(2*x+1,mid+1,R);    t[x].x=min(t[2*x].x,t[2*x+1].x);}void Pushdown(int x){    if (t[x].L==t[x].R||!t[x].tag) return;    int tag=t[x].tag;t[x].tag=0;    t[2*x].x+=tag;t[2*x].tag+=tag;    t[2*x+1].x+=tag;t[2*x+1].tag+=tag;}void change(int x,int L,int R,int y){    Pushdown(x);    if (t[x].L==L&&t[x].R==R){t[x].tag=y;t[x].x+=y;return;}    int mid=t[x].L+(t[x].R-t[x].L>>1);    if (R<=mid) change(2*x,L,R,y);else    if (L>mid) change(2*x+1,L,R,y);else    {change(2*x,L,mid,y);change(2*x+1,mid+1,R,y);}    t[x].x=min(t[2*x].x,t[2*x+1].x);}int ask(int x,int L,int R){    Pushdown(x);    if (t[x].L==L&&t[x].R==R) return t[x].x;    int mid=t[x].L+(t[x].R-t[x].L>>1);    if (R<=mid) return ask(2*x,L,R);else    if (L>mid) return ask(2*x+1,L,R);else    return min(ask(2*x,L,mid),ask(2*x+1,mid+1,R)); }int main(){    freopen("exam.in","r",stdin);    freopen("exam.out","w",stdout);    n=_read();m=_read();h=_read();    for (int i=1;i<=m;i++) b[i]=_read();    for (int i=1;i<=n;i++) a[i]=_read();    sort(b+1,b+1+m);    Build(1,1,m);    for (int i=1;i<m;i++){        int w=find(h-a[i]);        if (w<=m) change(1,w,m,1);    }    for (int i=m;i<=n;i++){        int w=find(h-a[i]);        if (w<=m) change(1,w,m,1);        if (ask(1,1,m)>=0) ans++;        w=find(h-a[i-m+1]);        if (w<=m) change(1,w,m,-1);    }    printf("%d\n",ans);    return 0;}
阅读全文
0 0