1. 程式人生 > >ARC076 F Exhausted? Hall定理 + 線段樹掃描線

ARC076 F Exhausted? Hall定理 + 線段樹掃描線

targe get alt build ext bcf pac urn 多少

~~~題面~~~

題目大意:

  有n個人,m個座位,每個人可以匹配的座位是[1, li] || [ri, m],可能有人不需要匹配座位(默認滿足),問最少有多少人不能被滿足。

題解:

  首先可以看出這是一個二分圖匹配,根據hall定理,我們只需要求出max(人的子集大小 - 被選出的人可以選的座位集合大小)。

  但是枚舉人的復雜度太高,所以考慮枚舉座位集合,因為每個人的可選區間都是一段前綴or後綴,因此要表達一個合法的座位集合,我們只需要所有人中最右邊的li和最左邊的ri即可。

  如圖所示:

  技術分享圖片

  因此這個時候要使得盡可能接近max,就要把所有可選區間不超過我們當前枚舉的區間的人都加進來。

  可以使用掃描線,求出對於每個R,所有的L相對應的值。

  

技術分享圖片
  1 #include<bits/stdc++.h>
  2 using namespace std;
  3 #define R register int
  4 #define AC 400100
  5 #define ac 1601000//error!!!數據範圍是2 00000, 不是1開頭!!!
  6 
  7 int n, m, ans = -INT_MAX, w;
  8 int Head[AC], Next[ac], date[ac], tot;
  9 int tree[ac], lazy[ac], l[ac], r[ac], l_[AC], r_[AC];
10 11 inline int read() 12 { 13 int x = 0;char c = getchar(); 14 while(c > 9 || c < 0) c = getchar(); 15 while(c >= 0 && c <= 9) x = x * 10 + c - 0, c = getchar(); 16 return x; 17 } 18 19 inline void add(int f, int w) 20 { 21 date[++tot] = w, Next[tot] = Head[f], Head[f] = tot;
22 } 23 24 inline void upmax(int &a, int b) 25 { 26 if(b > a) a = b; 27 } 28 29 void pre() 30 { 31 n = read(), m = read(); 32 for(R i = 1; i <= n; i ++) 33 l_[i] = read(), r_[i] = read(), add(r_[i], i); 34 } 35 36 inline void pushdown(int x) 37 { 38 if(lazy[x]) 39 { 40 int ll = x * 2, rr = ll + 1; 41 lazy[ll] += lazy[x], lazy[rr] += lazy[x]; 42 tree[ll] += lazy[x], tree[rr] += lazy[x];//這裏因為是+=,所以必須用lazy[x],不然會將lazy[ll]中的一些東西重復統計 43 lazy[x] = 0;//最後才清空!!!!!!!!!! 44 }//error!!!是區間加,不是賦值,不能直接覆蓋,要+= 45 } 46 47 inline void update(int x) 48 { 49 tree[x] = max(tree[x * 2], tree[x * 2 + 1]); 50 } 51 52 void build(int x, int ll, int rr) 53 { 54 l[x] = ll, r[x] = rr; 55 if(ll == rr) 56 { 57 tree[x] = -ll + 1; 58 return ; 59 } 60 int mid = (ll + rr) >> 1; 61 build(x * 2, ll, mid); 62 build(x * 2 + 1, mid + 1, rr); 63 update(x); 64 } 65 66 void change(int x, int ll, int rr) 67 { 68 pushdown(x); 69 if(l[x] == ll && r[x] == rr) 70 { 71 lazy[x] += w, tree[x] += w; 72 return ; 73 } 74 int mid = (l[x] + r[x]) >> 1; 75 if(rr <= mid) change(x * 2, ll, rr); 76 else if(ll > mid) change(x * 2 + 1, ll, rr); 77 else 78 { 79 change(x * 2, ll, mid); 80 change(x * 2 + 1, mid + 1, rr); 81 } 82 update(x); 83 } 84 85 void find(int x, int ll, int rr) 86 { 87 pushdown(x); 88 if(l[x] == ll && r[x] == rr) 89 { 90 upmax(ans, tree[x]); 91 return ; 92 } 93 int mid = (l[x] + r[x]) >> 1; 94 if(rr <= mid) find(x * 2, ll, rr); 95 else if(ll > mid) find(x * 2 + 1, ll, rr); 96 else find(x * 2, ll, mid), find(x * 2 + 1, mid + 1, rr);//這是取max啊,,,, 97 } 98 99 void work() 100 { 101 int now; 102 ans = n - m;//r = 0的情況 103 for(R i = Head[m + 1]; i; i = Next[i]) 104 { 105 now = date[i], w = 1; 106 change(1, l_[now] + 1, m + 1); 107 //find(1, 4, 4); 108 } 109 //find(1, 4, 4); 110 upmax(ans, tree[1]); 111 for(R i = m; i; -- i) 112 { 113 w = -1; 114 change(1, 1, i + 1); 115 for(R j = Head[i]; j; j = Next[j]) 116 { 117 now = date[j], w = 1; 118 change(1, l_[now] + 1, i + 1); 119 } 120 find(1, 1, i + 1);//左端點在後面就不合法了 121 } 122 printf("%d\n", ans); 123 } 124 125 int main() 126 { 127 freopen("in.in", "r", stdin); 128 pre(); 129 build(1, 1, m + 1);//要多出一位來代表左端點取0的情況 130 work();//ri最大居然可以到m+1... 131 fclose(stdin); 132 return 0; 133 }
View Code

ARC076 F Exhausted? Hall定理 + 線段樹掃描線