1. 程式人生 > 其它 >AT2645 [ARC076D] Exhausted? hall定理+線段樹題解

AT2645 [ARC076D] Exhausted? hall定理+線段樹題解

AT2645 [ARC076D] Exhausted? 

洛谷傳送門 AtCoder傳送門

題目大意:

有$m$個椅子在數軸上排列,第$i$張椅子的座標為$i$。

高橋君和他的朋友一共有$n$個人。高橋君他們因為玩了太久的遊戲,大家的腰和背都很痛,所以他們很有必要坐在椅子上休息一下。

高橋君他們每個人坐的椅子的座標都很講究,第 $i$ 個人想坐在座標在 $l_i$以下(包括$i$的椅子上),或者坐在座標在$r_i$以上(包括$r_i$的椅子上。當然,一個的椅子只能坐一個人。

可這樣計算下去,可能會讓他們不能都坐在椅子上休息。青木君關心高橋君他們的健康,儘可能多地增加椅子,讓高橋君他們都能夠坐在椅子上休息。 椅子可以新增到任意的實數座標上,請求出需要新增椅子數量的最小值。

簡述:

$n$個人,$m$個椅子,每個人能坐在$[1,l_i] \cup [r_i,m]$的椅子。求最少幾個人不能坐在椅子上。($1 \leq n,m \leq 200000$)

(這道題可以用貪心來做,詳見大佬的部落格)

我們可以把這個當成一個二分圖,兩個點集分別為人和椅子,求$n-最大匹配$。

但是資料範圍不允許直接建圖,於是我們利用Hall定理。

二分圖的Hall定理

Hall定理內容:對於一個二分圖$G(X,Y) (|X| \leq |Y|)$,存在完美匹配(匹配個數等於$|X|$)當且僅當對於$X$的任意子集$S$,$S$的鄰居個數$|N(S)|$必須大於等於$|S|$。

別問我為什麼,因為我也不知道。。。感性理解 

 百度百科的證明

Hall定理推論:二分圖$G(X,Y)$的最大匹配為$|X|-max_{S \subset X}(|S|-|N(S)|)$。

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

考慮表示$S$的鄰居點集,$N(S)=\bigcup_{i \in S} [1,l_i]\cup[r_i,m]$

這樣不能保證連續,不好表示,考慮計算不能坐的椅子的交集,最後取補集即為$N(S)$.

$|N(S)|=m-|\bigcap_{i \in S} (l_i,r_i)|$.

所以$ans$可以表示為$ans=max(|S|+|\bigcap_{i \in S} (l_i,r_i)|)-m$.

考慮線段樹和掃描線求最值,按照$l_i$排序,從小至大邊插入邊計算。

設當前左右端點為$L,R$(開區間).則當前答案為$max_{r \in (L,R]} (r-L-1+num(r,m))=max_{r \in (L,R]} (r+num(r,m))-L-1$,其中$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,ml=0,mr=M+1;
    for(int i=1;i<=N;i++) ml=max(ml,k[i].l),mr=min(mr,k[i].r);
    ans+=max(0,mr-ml-1);
    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;
}