1. 程式人生 > >【題解】 AtCoder ARC 076 F - Exhausted? (霍爾定理+線段樹)

【題解】 AtCoder ARC 076 F - Exhausted? (霍爾定理+線段樹)

r+ uil mat amp con pan com cnblogs 找出最大值

題面

題目大意:

給你\(m\)張椅子,排成一行,告訴你\(n\)個人,每個人可以坐的座位為\([1,l]\bigcup[r,m]\),為了讓所有人坐下,問至少還要加多少張椅子。

Solution:

  • 為什麽加椅子?我們可以在最左邊或最右邊一直加直到人人都有座位。
  • 首先這道題目抽象成二分圖很簡單,然後我們可以只要求解出人與座位的最大匹配是多少,總人數減去即可,但跑二分圖最大匹配顯然會超時,我們就可以往霍爾定理方面想。
  • 然後你還需要知道一個霍爾定理推論:假設某個人的集合為\(X\),這個集合所對應的椅子的集合為\(Y\),如果\(|X|\leq|Y|\),則具有完美匹配,如果\(|X|\geq|Y|\)
    ,則\(X\)至少要刪去\(|X|-|Y|\)個元素,才能有完備匹配,我們定義\(\Gamma(X)=|X|-|Y|\)

技術分享圖片

在這題裏,這個就是至少需要添加的椅子數目,所以我們要找出最大的\(\Gamma(X)\)

  • 接下來我們就來分析怎麽找出最大的\(\Gamma(X)\),因為\(X\)不具有任何性質,不好下手,我們考慮\(Y\)有啥特點,他一定是\([1,l]\bigcup[r,m]\),然後我們可以通過這個\(Y\)確定\(|X|\),所以會有一下做法

法一:暴力枚舉

  • 暴力枚舉\(l,r\),椅子的個數為\(l+m-r+1\),通過上面的定理處理出答案,找出\(\Gamma\)
    最大值。時間復雜度:\(O(n^2)\)貌似會更高到三方

法二:與其說是法二不如說是法一優化

  • 我們考慮上面的算法哪一些地方是冗余的。假設我們枚舉出\(l\),根據法一,我們會枚舉出所有的\(r\),顯然不用枚舉所有的,實際上只有找出這個\(l\)對應後面的\(r\)算出來的最大值,如何快速查詢出最大值,我們考慮使用線段樹。
  • 具體如何優化這個問題:計算式子是人數\(-(l+m-r+1)\)
  • 因為固定了\(l\),我們可以確定現在最多有多少人,總人數與\(l'\)有關(\(l'<l\))所以我們把\(l\)從小到大排序,把人數不斷往線段樹裏面丟。
  • 具體多少人根據\(r\)
    決定,某一個人要加入計算的人數內,必須是他所允許的所有椅子範圍都在討論範圍內,比如這個\(l=l_i\),那麽\(r<=r_i,r>=0\)的人數都可以加一(因為我們此時\(l\)是固定的,保證了這個人\(l\)在範圍內,如果枚舉的\(r\geq r_i\),那麽這個人數是要算進去的)。
  • 如果上面你看懂了,那就很好辦了,我們會發現對於枚舉的\(r\),式子中的人數\(-m-1+r\)是一樣的,人數我們是在推進\(l\)同時加入線段樹,\(-m-1+r\)我們就可以提前加入線段樹(建樹)
  • 對於一種特殊的情況,比如說:\(l_i=1,r_i=1\),那麽他能放的範圍為所有,所以我們要判斷\(m-n\)為答案的情況
  • 對於普通枚舉\(l\),我們沒必要考慮全部集合情況,所以我們就只用在線段樹\(l+2\rightarrow m+1\)找出最大值減去\(l\),得到的答案與\(ans\)\(max\)\(ok\)

  • 下面我們來將亂搞做法,我們把人按\(l\)排序,枚舉到人後把他可以座椅子的範圍用線段樹標記,然後算出總可以坐的椅子數與人數相比較,求出每次\(ans\)\(max\),一開始錯兩個點,然後排了兩個序,算兩次就只錯一個點了。這個方法顯然是不對噠!!因為顯然有一些人的子集沒有枚舉到。

這道題好理解吧(毛線,我起碼看了一個下午才看懂,去吃飯的路上突然懂了23333)

對了貪心也可以過這道題,只不過做法沒這麽優美罷了

Attention:

  1. 線段樹是從\(0\rightarrow m+1\),因為人的\(l,r\)包含\(0,m+1\)
  2. 特殊情況,\(|X|=n\)
  3. 枚舉出\(l\)後,找最大值一定是從\([l+1,m+1]\)中找

    First, 你枚舉的\(r\)\(>l\)
    Second,\(|X|=n\)已經被考慮(沒那個必要了,其實你從\(l+1\)開始也不影響)

Code:

//It is coded by ning_mew on 7.16
#include<bits/stdc++.h>
#define ls(x) (x*2)
#define rs(x) (x*2+1)
using namespace std;

const int maxn=2e5+7,inf=1e9+7;

int n,m,ans=0;
struct Opt{int l,r;}p[maxn];
struct Node{int lazy,maxx;}node[maxn*4];

bool cmp(const Opt &A,const Opt &B){return A.l<B.l;}
void pushdown(int num,int nl,int nr){
  if(!node[num].lazy)return;
  int lz=node[num].lazy; node[num].lazy=0;
  node[ls(num)].maxx+=lz;node[ls(num)].lazy+=lz;
  node[rs(num)].maxx+=lz;node[rs(num)].lazy+=lz;    
}
void update(int num){node[num].maxx=max(node[ls(num)].maxx,node[rs(num)].maxx);}
void build(int num,int nl,int nr){
  node[num].lazy=0; if(nl==nr){node[num].maxx=nr-m-1;return;}
  int mid=(nl+nr)/2;
  build(ls(num),nl,mid);build(rs(num),mid+1,nr);
  update(num);
}
void add(int num,int nl,int nr,int ql,int qr,int ad){
  if(ql<=nl&&nr<=qr){node[num].maxx+=ad;node[num].lazy+=ad;return;}
  if(nr<ql||qr<nl)return;
  int mid=(nl+nr)/2;pushdown(num,nl,nr);
  add(ls(num),nl,mid,ql,qr,ad);add(rs(num),mid+1,nr,ql,qr,ad);
  update(num);return;
}
int quary(int num,int nl,int nr,int ql,int qr){
  if(ql<=nl&&nr<=qr)return node[num].maxx;
  if(nr<ql||qr<nl)return -inf;
  int mid=(nl+nr)/2;pushdown(num,nl,nr);
  return max(quary(ls(num),nl,mid,ql,qr),quary(rs(num),mid+1,nr,ql,qr));
}
int main(){
  scanf("%d%d",&n,&m);
  for(int i=1;i<=n;i++)scanf("%d%d",&p[i].l,&p[i].r);
  sort(p+1,p+n+1,cmp);p[n+1].l=inf;
  build(1,0,m+1);
  for(int i=1;i<=n;i++){
    add(1,0,m+1,0,p[i].r,1);
    if(p[i].l!=p[i+1].l&&p[i].l<=m-1)ans=max(ans,quary(1,0,m+1,p[i].l+2,m+1)-p[i].l);
  }ans=max(ans,n-m);printf("%d\n",ans);
  return 0;
}

博主蒟蒻,隨意轉載。但必須附上原文鏈接:http://www.cnblogs.com/Ning-Mew/,否則你會場場比賽暴0!!!

【題解】 AtCoder ARC 076 F - Exhausted? (霍爾定理+線段樹)