[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;
}

猜你喜欢

转载自www.cnblogs.com/cyf32768/p/12196190.html