• 【ARC076F】 Exhausted


    hall定理大概是匈牙利的理论基础吧

    hall定理的内容:二分图(G)的的左部点点集为( m X),右部点点集为( m Y),设(| m X|leq |Y|),则二分图(G)存在完美匹配,即匹配个数为(| m X|)当且仅当,对于( m X)的任一子集( m X'),满足(| m X'|leq | m Nb(X')|)( m Nb(X'))( m X')的邻居点集

    必要性显然,考虑证明其充分性。

    使用反证法,对于一张满足hall定理的二分图,设其不存在完美匹配,我们找到( m X)中一个不在最大匹配中的点,由于满足hall定理这个点一定连向了( m Y)中某些点,又因为该点不在最大匹配中,那么其连向的点都已经跟其他点匹配;我们再把跟这些点匹配的点考虑进来,根据hall定理,我们还能找到一些跟这些点相连但已经匹配的点,再把跟这些匹配的点考虑进来;最后不难推出,不存在满足hall定理但不存在完美匹配的二分图。

    hall定理还有一条推论:二分图(G)的最大匹配为(| m X|-max(|X'|-|Nb(X')|)),这条推论对于我们求二分图最大匹配有一定启发

    对于这道题,可以显然的构造一个二分图模型,答案就是n-最大匹配;

    直接建图是不可取的,考虑使用上面的推论,我们需要找到一个( m X')使得( m |X'|-|Nb(X')|)即可

    对于第(i)个人他可以连边的区间是([1,l_i]cup [r_i,m]),由于( m Nb(X))本质上是点集的并,而这样不是很好考虑;于是我们做一个简单的转化,我们求一下不能连边的区间的交,对全集取一个补集得到的就是区间的并

    于是我们的目标是找到一个集合(S),最大化

    [|S|-(m-|cap_{iin S}(l_i,r_i)|)=|S|+|cap_{iin S}(l_i,r_i)|-m ]

    先来考虑没有交的情况,那么只需要让(|S|)最大,于是答案就是(n-m)

    再来考虑有交的情况,之后对于当前的一个区间((l_i,r_i))我们考虑交的左端点为(l_i)的时候的答案是多少,那么交为((l_i,k))的时候的答案就是(k-l_i+sum [r_jgeq k][l_jleq l_i]);于是把区间排序之后线段树+扫描线维护即可。

    代码

    #include<bits/stdc++.h>
    #define re register
    #define min(a,b) ((a)<(b)?(a):(b))
    const int maxn=2e5+5;
    struct Seg{int x,y;}a[maxn];
    int n,m,ans;
    inline int cmp(const Seg A,const Seg B) {return A.x==B.x?A.y>B.y:A.x<B.x;}
    int l[maxn<<2],r[maxn<<2],tag[maxn<<2],mx[maxn<<2];
    inline int max(int a,int b) {return a>b?a:b;}
    inline void add(int i,int v) {tag[i]+=v,mx[i]+=v;}
    inline void pushup(int i) {mx[i]=max(mx[i<<1],mx[i<<1|1]);}
    void build(int x,int y,int i) {
    	l[i]=x,r[i]=y;tag[i]=0;if(x==y){mx[i]=x;return;}
    	int mid=x+y>>1;build(x,mid,i<<1),build(mid+1,y,i<<1|1);pushup(i);
    }
    inline void pushdown(int i) {
    	if(!tag[i]) return;
    	add(i<<1,tag[i]),add(i<<1|1,tag[i]);tag[i]=0;
    }
    void change(int x,int y,int v,int i) {
    	if(x<=l[i]&&y>=r[i]) {add(i,v);return;}
    	int mid=l[i]+r[i]>>1;pushdown(i);
    	if(x<=mid) change(x,y,v,i<<1);
    	if(y>mid) change(x,y,v,i<<1|1);
    	pushup(i);
    }
    int query(int x,int y,int i) {
    	if(x<=l[i]&&y>=r[i]) return mx[i];
    	int mid=l[i]+r[i]>>1;pushdown(i);
    	if(y<=mid) return query(x,y,i<<1);
    	if(x>mid) return query(x,y,i<<1|1);
    	return max(query(x,y,i<<1),query(x,y,i<<1|1));
    }
    int main() {
    	scanf("%d%d",&n,&m);
    	for(re int i=1;i<=n;i++) scanf("%d%d",&a[i].x,&a[i].y);
    	std::sort(a+1,a+n+1,cmp);int nl=0,nr=m+1;
    	for(re int i=1;i<=n;i++) nl=max(nl,a[i].x),nr=min(nr,a[i].y);
        ans=n+(nl<nr?nr-nl-1:0);
    	build(0,m+1,1);
    	for(re int i=1;i<=n;i++) {
    		int nw=query(a[i].x+1,a[i].y,1);
    		ans=max(ans,nw-a[i].x);
    		change(0,a[i].y,1,1);
    	}
    	printf("%d
    ",ans>m?ans-m:0);return 0;
    }
    
  • 相关阅读:
    js隐藏嵌入表边框
    把字符串中的小写字母转换成大写字母
    字符串逆序
    嵌入式C语言编程与AVR技巧(一)——C语言环境访问MCU寄存器
    寻找第K大的数的方法总结
    ASCII码(全)
    把字符串中的小写字母转换成大写字母
    纯C 字符串操作函数 实现 (strcpy, strncpy, memcpy, memset, strcat, strlen ... ) .
    ASCII码(全)
    字符串逆序
  • 原文地址:https://www.cnblogs.com/asuldb/p/12023409.html
Copyright © 2020-2023  润新知