zoukankan      html  css  js  c++  java
  • 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
    ", 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
  • 相关阅读:
    影子的宽度&&盒子的个数
    【NOIP2017】【洛谷3958】奶酪cheese(并查集)(dfs)
    【USACO Jan 2011】【洛谷P3008】道路和航线 Roads and Planes
    增肥计划
    【洛谷1379】八数码
    【洛谷1985】【USACO07OPEN】翻转棋
    【NOI1995】极值问题
    车的放置
    【AtCoder
    Design Tutorial: Inverse the Problem
  • 原文地址:https://www.cnblogs.com/ww3113306/p/9732624.html
Copyright © 2011-2022 走看看