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