• Hall 定理


    Hall 定理 是匈牙利算法的基础

    大意是说,对于一个二分图

    左边的集合记为X,右边的集合记为Y

    存在完美匹配,(即匹配数目=min(|X|,|Y|))的充分必要条件是

    对于任意一个X的子集,设大小为k,那么和这个子集相连的Y必须不小于k个

    这里有一个十分直观的证明

    http://blog.csdn.net/werkeytom_ftd/article/details/65658944

    【No Name Problem】

    给出左右两边20个点的二分图以及一些边,每个点有点权

    两边选出点集,要求可以选出一些边,使得子图的每一个点能且仅能被一条边覆盖

    并且点权值和大于等于给定的值

    【Solution】

    我们可以对两边分别进行Hall定理统计,然后扫描一遍即可。

    考虑加入左边的X集合以及任意一条匹配边,然后考虑加入Y集合

    每次加入一个点,可能出现几种情况

    1、已经被覆盖

    2、加入匹配边,与之对应的不是X集合中点

    3、匹配边对应的是X集合中的点,那么我们不断向前寻找,可知路径上每一个Y集合的点都是由于原因1造成的,所以我们可以调整成为2方案

    这样就成了充分必要条件了。

    然后扫描统计即可

    #include <map>
    #include <cmath>
    #include <queue>
    #include <cstdio>
    #include <cstring>
    #include <iostream>
    #include <algorithm>
    using namespace std;
    #define F(i,j,k) for (int i=j;i<=k;++i)
    #define D(i,j,k) for (int i=j;i>=k;--i)
    #define ll long long
    #define mp make_pair
    int fa[1<<21],fb[1<<21],n,m;
    ll ta[1<<21],tb[1<<21];
    int tpa,tpb;
    int ca[21],cb[21],va[21],vb[21],t;
    char s[51];
    
    void print(int x)
    {
    	F(i,0,n-1) printf("%d",(x>>i)&1);
    	printf("
    ");
    }
    
    void solve()
    {
    	F(i,0,(1<<n)-1)
    	{
    //		printf("now is "); print(i);
    		ll val=0; fa[i]=1; int cta=0,ctb=0;
    		F(j,0,n-1) if (i&(1<<j))
    		{
    //			printf("& with %d ",fa[i^(1<<j)]); print(i^(1<<j));
    			fa[i]&=fa[i^(1<<j)],cta++,val+=va[j];
    		}
    		for (int j=0;j<m;++j) if (cb[j]&i) ++ctb;
    		if (ctb<cta) fa[i]=0;
    //		printf("ca %d cb %d
    ",cta,ctb);
    		if (fa[i]) ta[++tpa]=val;
    	}
    	sort(ta+1,ta+tpa+1);
    //	F(i,1,tpa) printf("%lld ",ta[i]); printf("
    ");
    	F(i,0,(1<<m)-1)
    	{
    		ll val=0; fb[i]=1; int cta=0,ctb=0;
    		F(j,0,m-1) if (i&(1<<j))
    			fb[i]&=fb[i^(1<<j)],cta++,val+=vb[j];
    		for (int j=0;j<n;++j) if (ca[j]&i) ++ctb;
    		if (ctb<cta) fb[i]=0;
    		if (fb[i]) tb[++tpb]=val;
    	}
    	sort(tb+1,tb+tpb+1);
    //	F(i,1,tpb) printf("%lld ",tb[i]); printf("
    ");
    }
    
    void cal()
    {
    	int p=tpb; ll ans=0;
    	F(i,1,tpa)
    	{
    		while (ta[i]+tb[p]>=t&&p>=1) p--;
    		ans+=tpb-p;
    	}
    	printf("%lld
    ",ans);
    }
    
    void Finout()
    {
    	freopen("guard.in","r",stdin);
    	freopen("guard.out","w",stdout);
    }
    
    int main()
    {
    	Finout();
    	scanf("%d%d",&n,&m);
    	F(i,0,n-1)
    	{
    		scanf("%s",s);
    		F(j,0,m-1)
    			if (s[j]=='1') ca[i]|=1<<j,cb[j]|=1<<i;
    	}
    //	F(i,0,n-1) printf("peoa %d ",i),print(ca[i]);
    //	F(i,0,m-1) printf("peob %d ",i),print(cb[i]);
    	F(i,0,n-1) scanf("%d",&va[i]);
    	F(i,0,m-1) scanf("%d",&vb[i]);
    	solve();
    	scanf("%d",&t);
    	cal();
    	return 0;
    }
    

      

    【BZOJ 2138】

    给定不包含的一些区间,每堆有一些石子,问每次从选定的区间内取K[i]个,在满足前面最大的情况下,当前最多能取出多少石子

    【Solution】

    考虑Hall定理,按照区间左端点排序,判定时只需要判定一个区间(删除永远不会用到的区间)

    所以我们可以得到

    $sum _{i=l}^{r} b_i <= sum _{i=L[l]}^{R[r]} a_i$

    然后用前缀和变换一下

    $c[i]=sb[i]-sa[R[i]]$
    $d[i]=sb[l-1]-sa[L[l]-1]$

    然后要求$c[r]<=d[l]$

    然后我们考虑操作对上述条件的影响。

    我们可以

    令$x=min(iepsilon[1,t] d[i])-max(iepsilon[t,m]c[i])$

    然后令$b[i]=x$

    然后再线段树上维护即可

    #include <bits/stdc++.h>
    using namespace std;
    #define maxn 100005
     
    int a[maxn],l[maxn],r[maxn],k[maxn],op[maxn],ban[maxn],dim[maxn];
    int sa[maxn],c[maxn],d[maxn],rk[maxn],pos[maxn];
    int data[2][maxn<<2],mx[2][maxn<<2],mn[2][maxn<<2],tag[2][maxn<<2];
     
    long long sqt(int x)
    {
        return x*x;
    }
     
    bool cmp(int a,int b)
    {return l[a]==l[b]?r[a]<r[b]:l[a]<l[b];}
     
    void make_tag(int id,int o,int f)
    {
        tag[id][o]+=f;
        mx[id][o]+=f;
        mn[id][o]+=f;
    }
     
    void pushdown(int o)
    {
        for (int i=0;i<2;++i)
            if (tag[i][o]!=0){
                make_tag(i,o<<1,tag[i][o]);
                make_tag(i,o<<1|1,tag[i][o]);
                tag[i][o]=0;
            }
    }
     
    void update(int o)
    {
        for (int i=0;i<2;++i)
            mx[i][o]=max(mx[i][o<<1],mx[i][o<<1|1]),
            mn[i][o]=min(mn[i][o<<1],mn[i][o<<1|1]);
    }
     
    void build(int o,int l,int r)
    {
        if (l==r){
            data[0][o]=mx[0][o]=mn[0][o]=c[l];
            data[1][o]=mx[1][o]=mn[1][o]=d[l];
            tag[0][o]=tag[1][o]=0;
            return ;
        }
        int mid=(l+r)>>1;
        build(o<<1,l,mid);
        build(o<<1|1,mid+1,r);
        update(o);
    }
     
    void modify(int id,int o,int l,int r,int L,int R,int f)
    {
        if (L>R) return ;
        if (L<=l&&r<=R){
            make_tag(id,o,f);
            return ;
        }
        int mid=(l+r)>>1; pushdown(o);
        if (R>mid) modify(id,o<<1|1,mid+1,r,L,R,f);
        if (L<=mid) modify(id,o<<1,l,mid,L,R,f);
        update(o);
    }
     
    int query(int id,int o,int l,int r,int L,int R,int f)
    {
        if (L<=l&&r<=R){
            if (f) return mx[id][o];
            else return mn[id][o];
        }
        int mid=(l+r)>>1; pushdown(o);
        if (R<=mid) return query(id,o<<1,l,mid,L,R,f);
        if (L>mid) return query(id,o<<1|1,mid+1,r,L,R,f);
        if (f) return max(query(id,o<<1,l,mid,L,R,f),query(id,o<<1|1,mid+1,r,L,R,f));
        else return min(query(id,o<<1,l,mid,L,R,f),query(id,o<<1|1,mid+1,r,L,R,f));
    }
     
    int main()
    {
         
        #ifdef WXL
        freopen("in.txt","r",stdin);
        freopen("wa.txt","w",stdout);
        #endif
         
        int n,x,y,z,p,m;
        scanf("%d%d%d%d%d",&n,&x,&y,&z,&p);
        for (int i=1;i<=n;++i) a[i]=(sqt(i-x)+sqt(i-y)+sqt(i-z))%p;
        scanf("%d",&m); if (m==0) return 0;
        scanf("%d%d%d%d%d%d",&k[1],&k[2],&x,&y,&z,&p);
        for (int i=1;i<=m;++i) scanf("%d%d",&l[i],&r[i]);
        for (int i=3;i<=m;++i) k[i]=(x*k[i-1]+y*k[i-2]+z)%p;
         
    //  for (int i=1;i<=n;++i) printf("%d ",a[i]); printf("
    ");
    //  for (int i=1;i<=m;++i) printf("%d ",k[i]); printf("
    ");
         
        for (int i=1;i<=m;++i) op[l[i]]++,op[r[i]+1]--;
        for (int i=1;i<=n;++i) op[i]+=op[i-1];
        for (int i=1;i<=n;++i){
            if (!op[i]) ban[i]+=1;
            dim[i]=dim[i-1]+ban[i];
        }
    //  for (int i=1;i<=n;++i) printf("%d ",dim[i]); printf("
    ");
        for (int i=1;i<=m;++i) l[i]-=dim[l[i]],r[i]-=dim[r[i]];
        for (int i=1;i<=n;++i) if (!ban[i]) a[i-dim[i]]=a[i];
        n-=dim[n];
         
    //  for (int i=1;i<=n;++i) printf("%d ",a[i]); printf("
    ");
        for (int i=1;i<=m;++i) rk[i]=i;
        sort(rk+1,rk+m+1,cmp);
        for (int i=1;i<=m;++i) pos[rk[i]]=i;
    //  for (int i=1;i<=m;++i) printf("%d %d
    ",l[rk[i]],r[rk[i]]); printf("
    ");
    //  for (int i=1;i<=m;++i) printf("%d ",rk[i]); printf("
    ");
        for (int i=1;i<=n;++i) sa[i]=a[i]+sa[i-1];
    //  for (int i=1;i<=n;++i) printf("%d ",sa[i]); printf("
    ");
        for (int i=1;i<=m;++i){
            c[i]=-sa[r[rk[i]]];
            d[i]=-sa[l[rk[i]]-1];
        }
         
    //  for (int i=1;i<=m;++i) printf("%d ",c[i]); printf("
    ");
    //  for (int i=1;i<=m;++i) printf("%d ",d[i]); printf("
    ");
     
        build(1,1,m);
         
        for (int i=1;i<=m;++i){
    //      printf("Option s %d
    ",i);
            int t=pos[i];
    //      printf("T = %d
    ",t);
            int D=query(1,1,1,m,1,t,0),C=query(0,1,1,m,t,m,1);
            int x=D-C;
    //      printf("%d %d %d %d
    ",C,D,D-C,x);
            x=min(x,k[i]);
            printf("%d
    ",x);
            modify(0,1,1,m,t,m,x);
            modify(1,1,1,m,t+1,m,x);
    //      for (int j=1;j<=m;++j) printf("%d ",query(0,1,1,m,j,j,0)); printf("
    ");
    //      for (int j=1;j<=m;++j) printf("%d ",query(1,1,1,m,j,j,0)); printf("
    ");
        }
         
    }
    

     

    【ARC 076 F】

    给定每个人的区间要求$<=l_i$ 或者$>=r_i$

    求最大匹配,$n<=10w$

    【Solution】

    考虑Hall定理,我们只需要考虑整个区间或者一首一尾两种情况

    整个区间比较容易计算最大不能匹配数目,略去

    对于一首一尾的情况即统计$l_i<=s$ && $r_i>=t$的数目

    然后对于每一个人看作$(l_i,r_i)$这个点

    然后用扫描线解决即可

    #include <bits/stdc++.h>
    using namespace std;
     
    #define maxn 200050
    int tag[maxn<<2],mx[maxn<<2];
     
    void update(int o)
    {
    	mx[o]=max(mx[o<<1],mx[o<<1|1]);
    }
     
    void make_tag(int o,int f)
    {
    	tag[o]+=f;
    	mx[o]+=f;
    }
     
    void pushdown(int o)
    {
    	if (tag[o]!=0){
    		make_tag(o<<1,tag[o]);
    		make_tag(o<<1|1,tag[o]);
    		tag[o]=0;
    	}
    }
     
    void modify(int o,int l,int r,int L,int R,int f)
    {
    //	printf("Modiy %d %d %d %d %d %d
    ",o,l,r,L,R,f);
    	if (L<=l&&r<=R){
    		make_tag(o,f);
    		return ;
    	}
    	int mid=(l+r)>>1; pushdown(o);
    	if (R>mid) modify(o<<1|1,mid+1,r,L,R,f);
    	if (L<=mid) modify(o<<1,l,mid,L,R,f);
    	update(o);
    }
     
    void build(int o,int l,int r)
    {
    	if (l==r){
    		mx[o]=l;
    		tag[o]=0;
    		return;
    	}
    	int mid=(l+r)>>1;
    	build(o<<1,l,mid);
    	build(o<<1|1,mid+1,r);
    	update(o);
    }
     
    int query(int o,int l,int r,int L,int R)
    {
    	if (L<=l&&r<=R) return mx[o];
    	int mid=(l+r)>>1; pushdown(o);
    	if (R<=mid) return query(o<<1,l,mid,L,R);
    	else if (L>mid) return query(o<<1|1,mid+1,r,L,R);
    	else return max(query(o<<1,l,mid,L,R),query(o<<1|1,mid+1,r,L,R));
    }
     
    vector <int> v[maxn];
     
    int main()
    {
    	
    	#ifdef WXL
    	freopen("in.txt","r",stdin);
    	#endif
    	
    	int n,m,ans;
    	scanf("%d%d",&n,&m); ans=max(0,n-m);
    	for (int i=1;i<=n;++i){
    		int x,y;
    		scanf("%d%d",&x,&y);
    		v[x].push_back(y);
    	}
    	build(1,0,m+1);
    	for (int i=0;i<=m;++i){
    		for (int j=0;j<(int)v[i].size();++j){
    //			printf("%d -- %d+1
    ",0,v[i][j]);
    			modify(1,0,m+1,0,v[i][j],1);
    		}
    //		printf("Mx on %d %d = %d
    ",i+1,m+1,query(1,0,m+1,i+1,m+1));
    //		printf("%d %d
    ",i,query(1,0,m+1,i+1,m+1));
    		ans=max(ans,query(1,0,m+1,i+1,m+1)-i-m-1);
    	}
    	printf("%d
    ",ans);
    	
    }
    

      

  • 相关阅读:
    OilPaint(转载/实验)
    UE4 3D artist
    render pipeline about (翻译)
    Python 相对导入 碎碎念
    USF, USH Grammar
    JZ19 顺时针打印矩阵
    JZ49 把字符串转换成整数
    JZ45 扑克牌顺子
    JZ53 表示数值的字符串
    JZ48 不用加减乘除做加法
  • 原文地址:https://www.cnblogs.com/SfailSth/p/7078247.html
Copyright © 2020-2023  润新知