题目大意
给你m个凳子,第i个的位置为i,现在有n个人要坐到凳子上,对于第i个人,它不能坐到位置在
(li,ri)
(
l
i
,
r
i
)
区间里的凳子。现在问要添加多少张凳子,才能使得所有人都能坐到凳子,添加的凳子可以放在实数位置。
n,m<=2e5,0<=l,r<=m+1.
题目分析
显然是一个二分图匹配问题。直接用n-最大匹配即可。
不知道zkw网络流在线段树优化连边的图里面能不能跑过(逃
我一直不知道怎么用霍尔定理…
霍尔定理:对于一个二分图,它存在完美匹配当且仅当:对于X部的任意子集S,记能和S联通的Y部的点集为T,满足|S|<=|T|。正确性显然嘛,你n个点完美匹配情况可以找一对点删去然后转化成子问题。反正归纳随便证。
这个也可以用来求最大匹配,即找到|S|-|T|的最大值mx,那么|S|-mx就是最大匹配数了,因为删掉|S|-mx个点之后,就存在完美匹配。
那么怎么应用呢?
暴力的话,我们枚举X部(人)的点集,然后计算出Y部(凳子)的点集T,维护最大值。
优化:考虑到T只会是凳子的一段前缀并上一段后缀,那么我们直接考虑枚举这个T,算出在此情况下最大的S来更新最大值。
那么这个用线段树维护一下就O(nlogn)了。
代码
#include<cstdio>
#include<algorithm>
#include<cstring>
#include<cmath>
#include<map>
using namespace std;
#define fo(i,j,k) for(i=j;i<=k;i++)
#define fd(i,j,k) for(i=j;i>=k;i--)
#define cmax(a,b) (a=(a>b)?a:b)
#define cmin(a,b) (a=(a<b)?a:b)
typedef long long ll;
typedef double db;
const int N=2e5+5;
int n,m,x,y,i,ans;
int tt,b[N],nxt[N],fst[N];
void cr(int x,int y)
{
tt++;
b[tt]=y;
nxt[tt]=fst[x];
fst[x]=tt;
}
int tr[N*4],tag[N*4];
void dw(int x,int s)
{
if (s)
{
tag[x*2]+=tag[x];
tag[x*2+1]+=tag[x];
}
tr[x]+=tag[x];
tag[x]=0;
}
void change(int x,int l,int r,int i,int j,int v)
{
if (l==i&&r==j)
{
tag[x]+=v;
dw(x,l!=r);
}else
{
int m=l+r>>1;
dw(x*2,l!=m);
dw(x*2+1,m+1!=r);
if (i<=m) change(x*2,l,m,i,min(m,j),v);
if (m<j) change(x*2+1,m+1,r,max(i,m+1),j,v);
tr[x]=max(tr[x*2],tr[x*2+1]);
}
}
int get(int x,int l,int r,int i,int j)
{
if (l==i&&r==j) return tr[x];
int m=l+r>>1,ret=0;
dw(x*2,l!=m);
dw(x*2+1,m+1!=r);
if (i<=m) ret=max(ret,get(x*2,l,m,i,min(m,j)));
if (m<j) ret=max(ret,get(x*2+1,m+1,r,max(i,m+1),j));
return ret;
}
void make(int x,int l,int r)
{
if (l==r)
tr[x]=-m+l;else
{
int m=l+r>>1;
make(x*2,l,m);
make(x*2+1,m+1,r);
tr[x]=max(tr[x*2],tr[x*2+1]);
}
}
int main()
{
freopen("t8.in","r",stdin);
freopen("t8.out","w",stdout);
scanf("%d %d",&n,&m);
fo(i,1,n)
{
scanf("%d %d",&x,&y);
cr(x,y);
}
ans=n-m;
m++;
make(1,0,m);// special shit
fo(i,0,m-1)
{
for(int p=fst[i];p;p=nxt[p])
change(1,0,m,i+1,b[p],1);
ans=max(ans,get(1,0,m,i+2,m)-i);
}
cmax(ans,0);
printf("%d\n",ans);
}