ARC076 F Exhausted? Hall定理 + 线段树扫描线

~~~题面~~~

题目大意:

  有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

 

posted @ 2018-09-30 17:13  ww3113306  阅读(398)  评论(0编辑  收藏  举报
知识共享许可协议
本作品采用知识共享署名-非商业性使用-禁止演绎 3.0 未本地化版本许可协议进行许可。