[agc076f]Exhausted?

题目大意

给你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);
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值