• [ZJOI2019]浙江省选(计算几何)


    Address

    Luogu P5328

    Solution

    • 这是什么神仙题
    • 蒟蒻编了一个辣鸡做法搞了一天才过
    • 把每个人看作直线 : \(y=a_ix+b_i\),枚举\(i=1→m\),并分别求出最好排名为 \(i\) 的直线集合。
    • 显然能拿 \(rank1\) 的直线必须位于下凸壳上。
    • 注意由于 \(x\) 必须为非负整数,这个下凸壳上的点必须都是整数,举个例子:某条直线在 \(0.5≤x≤0.8\) 的时候能拿 \(rank1\),那么这条直线不在下凸壳上。
    • 由于\(x∈(0,+∞)\),下凸壳由一些线段和最右的一条射线组成
    • 能拿 \(rank2\) 的直线必须满足:
      \(1.\) 除掉能拿 \(rank1\) 的直线之后,位于下凸壳上。
      \(2.\) 存在一个非负整数 \(x\),使得最多一条 \(rank1\) 直线在它之上。
    • 条件 \(1\) 十分简单,跟求能拿 \(rank1\) 的直线的方法一样。
    • 对于条件 \(2\):枚举每一条能拿 \(rank1\) 的直线 \(y=cx+d\),二分出两个非负整数\(l,r\),若凸壳用 \(y=f(x)\) 表示,则当 \(x∈[l,r]\) 时,\(cx+d>f(x)\)
    • 为降低时间复杂度,不用二分 \(l,r\) 的值,而是二分直线和凸壳“相交”于哪一条线段或射线。
    • 注意这个 \(r\) 可能无限大。
    • 然后把每条直线的 \(l,r\),以及每条线段(或射线)的端点(端点也应是非负整数,若端点为 \(u,v\),则表示 \(x∈[u,v]\) 时,这条线段(或射线)在凸壳上是 \(rank1\)。特别地,射线的右端点可以设为一个很大的数。)放入一个数组 \(h\) 进行排序离散化。
    • 离散化后,如果有 \(h[i]+1<h[i+1]\),再往 \(h\) 中加入 \(h[i]+1\)
    • 再次排序,离散化。
    • 把每条直线的 \(l,r\) 在离散化后的数组上做差分,即对于每个 \(h[i]\),算出 \(x=h[i]\) 时,有几条 \(rank1\) 的直线在凸壳之上(即点的被覆盖次数)。
    • 然后枚举凸壳上的所有线段(或射线),如果\(h\) 中所有位于此线段上的点中,被覆盖次数的最小值 \(<2\),则这条线段的最好排名为 \(rank2\)
    • 对于 \(rank3,rank4\),依此类推即可。
    • 还没结束!
    • \(a_i≤10^{9},b_i≤10^{18}\) 可知,此题我们不可以用 \(double\) 表示实数(毒瘤精度误差),要用分数表示实数,而且不能用假分数,要用带分数(表示为 \(a+\frac{b}{c}\),其中 \(0≤b<c\))。\(QAQ\)

    Code

    • 我的做法是不是太麻烦了
    #include <bits/stdc++.h>
    
    using namespace std;
    
    #define ll long long
    
    const int e = 1e6 + 5;
    const ll inf = 2e18;
    const double eps = 1e-12;
    int id[e], stk[e], top, n, st[e][20], m, ans[e], cnt, rk, s[e], p[e], w, logn[e], tot;
    ll a[e], b[e], h[e], g[e];
    struct frac
    {
       ll a, b, c;
       frac() {a = b = 0; c = 1;}
       frac(ll x, ll y)
       {
           if (y < 0) x = -x, y = -y;
           a = x / y; c = y; b = x % y;
           if (b < 0) b += y, a--;
       }
       inline ll floor() 
       {
           return a; 
       }
       inline ll ceil()
       {
           return b % c ? a + 1 : a;
       }
    }c[e];
    struct work
    {
       ll l, r;
    }q[e];
    
    template <class t>
    inline void read(t & res)
    {
       char ch;
       while (ch = getchar(), !isdigit(ch));
       res = ch ^ 48;
       while (ch = getchar(), isdigit(ch))
       res = res * 10 + (ch ^ 48);
    }
    
    inline bool operator > (frac a, frac b)
    {
       return a.a != b.a ? a.a > b.a : a.b * b.c > a.c * b.b;
    }
    
    inline bool cmp(int x, int y)
    {
       return a[x] != a[y] ? a[x] < a[y] : b[x] > b[y];
    }
    
    inline bool check(int p1, int p2, int p3)
    {
       ll k1 = ceil(1.0 * (b[p1] - b[p2]) / (a[p2] - a[p1])), 
           k2 = floor(1.0 * (b[p2] - b[p3]) / (a[p3] - a[p2]));
       return k1 > k2;
    }
    
    inline double cross(ll a1, ll b1, ll a2, ll b2)
    {
       return 1.0 * (b1 - b2) / (a2 - a1);
    }
    
    inline void rmq()
    {
       int i, j;
       logn[0] = -1;
       for (i = 1; i <= w; i++) logn[i] = logn[i >> 1] + 1, st[i][0] = s[i];
       for (j = 1; (1 << j) <= w; j++)
       for (i = 1; i + (1 << j) - 1 <= w; i++)
       st[i][j] = min(st[i][j - 1], st[i + (1 << j - 1)][j - 1]);
    }
    
    inline int ask(int l, int r)
    {
       int k = logn[r - l + 1];
       return min(st[l][k], st[r - (1 << k) + 1][k]);
    }
    
    inline bool judge(ll x, ll y, frac z)
    {
       if (x > 0) return z > frac(y, x);
       else return frac(-y, -x) > z;
    }
    
    inline void solve(int k)
    {
       int i; cnt = m = 0; top = 0;
       for (i = 1; i <= n; i++)
       if (ans[i] == -1) p[++m] = i;
       sort(p + 1, p + m + 1, cmp);
       for (i = 1; i <= m; i++)
       if (i == 1 || a[p[i]] != a[p[i - 1]]) id[++cnt] = p[i];
       for (i = 1; i <= cnt; i++)
       {
           while (top > 0 && b[stk[top]] < b[id[i]]) top--; 
           while (top > 1 && check(stk[top - 1], stk[top], id[i])) top--;	
           stk[++top] = id[i];
       }
       m = cnt = 0;
       for (i = 1; i <= top; i++) id[++m] = stk[i];
       if (k == 1)
       {
           for (i = 1; i <= top; i++) ans[stk[i]] = 1;
           return;
       }
       for (i = 1; i <= m; i++) 
       {
           if (i == 1) c[i] = frac(0, 1);
           else c[i] = frac(b[id[i]] - b[id[i - 1]], a[id[i - 1]] - a[id[i]]);
       }
       c[m + 1] = frac(inf, 1);
       for (i = 1; i <= n; i++)
       if (ans[i] != -1)
       {
           ll c1 = a[i], d1 = b[i]; 
           int p1 = m + 2, p2 = 0, l = 1, r = m + 1;
           while (l <= r)
           {
               int mid = l + r >> 1;
               if (mid == m + 1)
               {
                   if (c1 > a[id[m]] || (c1 == a[id[m]] && d1 > b[id[m]])) p1 = mid;
                   r = mid - 1;
                   continue;
               } 
               if (c1 == a[id[mid]] || judge(c1 - a[id[mid]], b[id[mid]] - d1, c[mid]))
               {
                   p1 = mid;
                   r = mid - 1;
               }
               else if (a[id[mid]] < c1) l = mid + 1;
               else r = mid - 1;
           }
           l = 1; r = m + 1;
           while (l <= r)
           {
               int mid = l + r >> 1;
               if (mid == m + 1)
               {
                   if (c1 > a[id[m]] || (c1 == a[id[m]] && d1 > b[id[m]])) p2 = mid;
                   l = mid + 1;
                   continue;
               }
               if (c1 == a[id[mid]] || judge(c1 - a[id[mid]], b[id[mid]] - d1, c[mid]))
               {
                   p2 = mid;
                   l = mid + 1;
               }
               else if (a[id[mid]] < c1) l = mid + 1;
               else r = mid - 1;
           }
           cnt++;
           if (p1 == 1) q[cnt].l = 0;
           else 
           {
               q[cnt].l = ceil(cross(a[id[p1 - 1]], b[id[p1 - 1]], c1, d1));
               if ((d1 - b[id[p1 - 1]]) % (a[id[p1 - 1]] - c1) == 0) q[cnt].l++;
           }
           if (p2 == m + 1) q[cnt].r = inf;
           else 
           {
               q[cnt].r = floor(cross(a[id[p2]], b[id[p2]], c1, d1));
               if ((d1 - b[id[p2]]) % (a[id[p2]] - c1) == 0) q[cnt].r--;
           }
           if (q[cnt].l > q[cnt].r) cnt--;
       }
       tot = 0;
       for (i = 1; i <= cnt; i++) h[++tot] = q[i].l, h[++tot] = q[i].r;
       for (i = 1; i <= m; i++) h[++tot] = c[i].ceil(), h[++tot] = c[i + 1].floor(); 
       sort(h + 1, h + tot + 1);
       w = 0;
       for (i = 1; i <= tot; i++)
       if (i == 1 || h[i] != h[i - 1]) g[++w] = h[i];
       sort(g + 1, g + w + 1);
       int w0 = w;
       for (i = 1; i < w0; i++)
       if (g[i] + 1 != g[i + 1]) g[++w] = g[i] + 1;
       sort(g + 1, g + w + 1);
       memset(s, 0, sizeof(s));
       for (i = 1; i <= cnt; i++)
       {
           int x = lower_bound(g + 1, g + w + 1, q[i].l) - g,
               y = lower_bound(g + 1, g + w + 1, q[i].r) - g;
           s[x]++;
           s[y + 1]--;
       }
       for (i = 1; i <= w; i++) s[i] += s[i - 1];
       rmq();
       for (i = 1; i <= m; i++)
       {
           ll x = c[i].ceil(), y = c[i + 1].floor(); 
           int px = lower_bound(g + 1, g + w + 1, x) - g,
               py = upper_bound(g + 1, g + w + 1, y) - g - 1;
           if (ask(px, py) < k) ans[id[i]] = k;
       }
    }
    
    int main()
    {
       read(n); read(rk);
       int i;
       for (i = 1; i <= n; i++) read(a[i]), read(b[i]), ans[i] = -1;
       for (i = 1; i <= rk; i++) solve(i);
       for (i = 1; i <= n; i++) printf("%d ", ans[i]);
       putchar('\n');
       return 0;
    }
    
  • 相关阅读:
    pycharm路径
    git常用命令
    分页
    router
    视图集
    Leanring TypeScript 中文版
    RXJS 系列 04
    RXJS 系列 03
    RXJS 系列 02
    RXJS 系列 01
  • 原文地址:https://www.cnblogs.com/cyf32768/p/12196190.html
Copyright © 2020-2023  润新知