题目链接:ARC076 F - Exhausted?
题意:
一行摆了 (M) 张椅子,第 (i) 张椅子的位于 (i) 的位置。有 (N) 个人想坐在椅子上休息,第 (i) 个人希望自己坐在一张坐标 (leq L_i) 或 (geq R_i) 的椅子,且一张椅子只能坐一个人。
由于不一定所有人都能坐到合适的椅子上,(Aoki) 会额外提供一些,这些椅子可以放在任意实数的坐标处,请问最少需要多少张额外的椅子。
(1leq N,M leq 2 imes 10^5)
(0leq L_i<R_ileq M+1(1leq ileq N))
思路:
把人和椅子分别作为左右部节点,那便是二分图匹配,答案为 (N) 减去最大匹配,不过这么做显然不够优秀,想一想还有什么别的计算最大匹配的方法,有!通过霍尔定理((Hall's) (Theorem)),这里的答案应为 (max{|X|-|Gamma (X)|}) 和 (0) 取较大值,(X) 为左部节点的任意一个子集, (Gamma (X)) 为 (X) 的邻居节点集。
题目中 (leq L_i) 和 (geq R_i) 的限制看起来很麻烦,把它取反,只需要维护 (min_{iin X}{R_i}) 和 (max_{iin X}{L_i}) ,我们把左边界 (L_i) 定下来,那么要求的即 (sum_{j=1}^N[L_jleq L_i][R_jgeq k]-(M-(k-L_i+1))) 的最大值,把 (M) 和 (-L_i+1) 扔出来,即使(sum_{j=1}^N[L_jleq L_i][R_jgeq k]+k) 最大,可以线段树扫描线维护,位置 (i) 的值初始为 (i) 本身(即 ("+k")),根据 (L_i) 从左往右扫描,处理完一个人就给 ([0,R_i]) 加 (1) ( (kin[0,R_i]) 时自己会被算进去),每次询问 ([L_i+1,R_i]) 上最大值( 由于是开区间 ,(L_i) 和 (k) 不能重合)。
注意到当 (Gamma (X)) 覆盖了整个 (M) 的区域时,(M-(k-L_i+1)) 这么算是不对的,所以要先另外处理好,作为 (ans) 的初始值即可。
Code:
#include<iostream>
#include<algorithm>
#define rep(i,a,b) for(int i=(a);i<=(b);i++)
#define per(i,b,a) for(int i=(b);i>=(a);i--)
#define N 200200
using namespace std;
struct guest{
int l,r;
}a[N];
struct SegmentTree{
int l,r,mx,lazy;
}t[4*N];
int n,m;
void build(int x,int l,int r){
t[x].l=l,t[x].r=r;
if(l==r){t[x].mx=l;return;}
int mid=(l+r)>>1;
build(x*2,l,mid),build(x*2+1,mid+1,r);
t[x].mx=max(t[x*2].mx,t[x*2+1].mx);
}
void pushdown(int x){
if(t[x].lazy){
t[x*2].mx+=t[x].lazy,t[x*2].lazy+=t[x].lazy;
t[x*2+1].mx+=t[x].lazy,t[x*2+1].lazy+=t[x].lazy;
t[x].lazy=0;
}
}
void update(int x,int left,int right,int k){
if(t[x].l>=left&&t[x].r<=right){
t[x].mx+=k,t[x].lazy+=k; return;
}
pushdown(x);
int mid=(t[x].l+t[x].r)>>1;
if(mid>=left)update(x*2,left,right,k);
if(mid<right)update(x*2+1,left,right,k);
t[x].mx=max(t[x*2].mx,t[x*2+1].mx);
}
int query(int x,int left,int right){
if(t[x].l>=left&&t[x].r<=right)return t[x].mx;
int mid=(t[x].l+t[x].r)>>1,res=0;
pushdown(x);
if(mid>=left)res=max(res,query(x*2,left,right));
if(mid<right)res=max(res,query(x*2+1,left,right));
return res;
}
bool cmp(guest a,guest b){return a.l<b.l;}
int main(){
ios::sync_with_stdio(false);
cin>>n>>m;
rep(i,0,n-1)cin>>a[i].l>>a[i].r;
build(1,0,m+1);
sort(a,a+n,cmp);
int lm=m+1,rm=0;
rep(i,0,n-1)lm=min(lm,a[i].r),rm=max(rm,a[i].l);
int ans=n+(rm<lm?lm-rm-1:0);
rep(i,0,n-1){
ans=max(ans,query(1,a[i].l+1,a[i].r)-a[i].l);
update(1,0,a[i].r,1);
}
cout<<max(ans-m,0)<<endl;
return 0;
}