AT2645 [ARC076D] Exhausted? hall定理+线段树题解

AT2645 [ARC076D] Exhausted? 

洛谷传送门 AtCoder传送门

题目大意:

m个椅子在数轴上排列,第i张椅子的坐标为i

高桥君和他的朋友一共有n个人。高桥君他们因为玩了太久的游戏,大家的腰和背都很痛,所以他们很有必要坐在椅子上休息一下。

高桥君他们每个人坐的椅子的坐标都很讲究,第 i 个人想坐在坐标在 li以下(包括i的椅子上),或者坐在坐标在ri以上(包括ri的椅子上。当然,一个的椅子只能坐一个人。

可这样计算下去,可能会让他们不能都坐在椅子上休息。青木君关心高桥君他们的健康,尽可能多地增加椅子,让高桥君他们都能够坐在椅子上休息。 椅子可以添加到任意的实数坐标上,请求出需要添加椅子数量的最小值。

简述:

n个人,m个椅子,每个人能坐在[1,li][ri,m]的椅子。求最少几个人不能坐在椅子上。(1n,m200000)

(这道题可以用贪心来做,详见大佬的博客)

我们可以把这个当成一个二分图,两个点集分别为人和椅子,求n

但是数据范围不允许直接建图,于是我们利用Hall定理。

二分图的Hall定理

Hall定理内容:对于一个二分图G(X,Y)(|X||Y|),存在完美匹配(匹配个数等于|X|)当且仅当对于X的任意子集SS的邻居个数|N(S)|必须大于等于|S|

别问我为什么,因为我也不知道。。。感性理解   百度百科的证明

Hall定理推论:二分图G(X,Y)的最大匹配为|X|maxSX(|S||N(S)|)

所以可以得出,ans=n(nmax(|S||N(S)|))=max(|S||N(S)|).

考虑表示S的邻居点集,N(S)=iS[1,li][ri,m]

这样不能保证连续,不好表示,考虑计算不能坐的椅子的交集,最后取补集即为N(S).

|N(S)|=m|iS(li,ri)|.

所以ans可以表示为ans=max(|S|+|iS(li,ri)|)m.

考虑线段树和扫描线求最值,按照li排序,从小至大边插入边计算。

设当前左右端点为L,R(开区间).则当前答案为maxr(L,R](rL1+num(r,m))=maxr(L,R](r+num(r,m))L1,其中num(a,b)代表右端点在[a,b]内的已计算的椅子数。

这样就好整多了,节点初值为下标,每处理一个椅子就把[0,R]的贡献加一。

代码并不难写。

const int maxn=2e5+5,maxt=maxn<<2;
int tr[maxt],tag[maxt],N,M;
#define ls rt<<1,l,m
#define rs rt<<1|1,m+1,r
#define m ((l+r)>>1)
void pushup(int rt){
    tr[rt]=max(tr[rt<<1],tr[rt<<1|1]);
}
void pushtag(int rt,int c){
    tag[rt]+=c;
    tr[rt]+=c;
}
void pushdown(int rt){
    if(tag[rt]){
        pushtag(rt<<1,tag[rt]),pushtag(rt<<1|1,tag[rt]);
        tag[rt]=0;
    }
}
void build(int rt,int l,int r){
    if(l==r){
        tr[rt]=l;
        return;
    }
    build(ls),build(rs);
    pushup(rt);
}
void ch(int rt,int l,int r,int L,int R,int w){
    if(r<L||R<l) return;
    if(L<=l&&r<=R){
        pushtag(rt,w);
        return;
    }
    pushdown(rt);
    ch(ls,L,R,w),ch(rs,L,R,w);
    pushup(rt);
}
int suan(int rt,int l,int r,int L,int R){
    if(r<L||R<l) return 0;
    if(L<=l&&r<=R) return tr[rt];
    pushdown(rt);
    return max(suan(ls,L,R),suan(rs,L,R));
}
struct chr{
    int l,r;
    friend bool operator<(chr a,chr b){
        return a.l==b.l?a.r>b.r:a.l<b.l;
    }
}k[maxn];
int MAIN(){
    cin>>N>>M;
    for(int i=1;i<=N;i++) scanf("%d%d",&k[i].l,&k[i].r);
    sort(k+1,k+N+1);
    build(1,0,M+1);
    int ans=N;
    for(int i=1;i<=N;i++){
        int l=k[i].l,r=k[i].r;
        ch(1,0,M+1,0,r,1);
        ans=max(ans,suan(1,0,M+1,l+1,r)-l-1);
    }
    cout<<max(0,ans-M)<<endl;
    return 0;
}

 

posted @   xxqz  阅读(181)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!
点击右上角即可分享
微信分享提示