IOI 2015 Teams 分组

\[made \ by \ Ameiyo \]


贪心做法

对于每组询问, 我们从小到大考虑 K ,那么每次肯定是在 A 小于等于 K 的 $ (A, B) $ 中选取最小的 KB ,因为更大的 B 可能后面会用到。

因此每次直接暴力可以得到 $ O(N * S * \log N) $ 的算法(先按 A 排序,然后维护 B 的值,可以考虑树状数组)。


优化之后的做法

我们转换一下题目,对于可以被 K 选择的 $ (A, B) $ ,因为要满足 $ A \le K $ 且 $ K \le B $ ,所以在坐标系中,点 $ (A, B) $ 就要在点 $ (K, K) $ 的左上方。所以对于某个 K ,满足 $ x \in [0, K] , y \in [K, + \infin) $ 的点是可以被其选择的,注意这些点都是在一个矩形中。

蓝色的是可选点

但是我们不能直接用所有满足条件的点,因为可能有的点已经被前面的矩形用过了!

对于当前的矩形,他一定会用掉能选的点中,纵坐标最小的 $ K_i $ 个点,记没被用掉的点中最小的纵坐标是 $ H_i $ (注意这里的 $ H_i $ 是指自己到上个矩形的范围内,因为自己所维护的矩形是这一段)。

(在下图中,虽然 Aj 可选但是没有选择的点中最低的,但是 B 的高度才是 $ H_j $ )

QfVsXR.png

那么对于一个 $ K_j \ge K_i $ 的 $ K_j $ ,如果 $ K_j > H_i $ ,那么由于可以被 $ j $ 使用的点 $ i $ 都没有用过,所以 $ j $ 可以直接使用;但如果 $ k_j \le H_i $ ,那么可能某些点(注意存在和 $ H_i $ 高度相同的点)已经被 $ i $ 用过了,所以就不能直接计数。

但是我们可以维护出 $ i $ 还没有用过的点的数量 $ rem_i $ ,这样由于 $ K_j < H_i $ ,所以 $ rem_i $ 这些点 $ j $ 也是可以直接用的,再加上横坐标在 $ (H_i, H_j] $ 之间的 $ j $ 可以使用的点,就是全部 $ j $ 可以使用的点。

注意到我们处理的 K 是单调不降的,所以可以用一个单调栈维护一个 H 单调递减的序列,每次处理之前,把 H 小于当前高度的矩形都出栈(注意只有小于的可以出栈),然后再通过前面的 rem 以及新的点数来判断是否可行并且维护。

因为这是一个二维平面上的计数问题,所以可以用主席树维护点的数量。

代码

int Rt[N], lson[M], rson[M], val[M], cntNode;
void Insert(int &rt, int pre, int l, int r, int x) {
    rt = ++cntNode;
    lson[rt] = lson[pre], rson[rt] = rson[pre];
    val[rt] = val[pre] + 1;
    if (l == r) return ;
    int mid = (l + r) >> 1;
    if (x <= mid) Insert(lson[rt], lson[pre], l, mid, x);
    else Insert(rson[rt], rson[pre], mid + 1, r, x);
}
int Query(int L, int R, int l, int r, int k) { // (cnt) >= k
    if (l == r) return val[R] - val[L];
    int mid = (l + r) >> 1, sum = val[rson[R]] - val[rson[L]];
    if (k > mid) return Query(rson[L], rson[R], mid + 1, r, k);
    else return sum + Query(lson[L], lson[R], l, mid, k);
}
int Queryk(int L, int R, int l, int r, int k) { // kth
    if (l == r) return l;
    int mid = (l + r) >> 1, sum = val[rson[R]] - val[rson[L]];
    if (k > sum) return Queryk(lson[L], lson[R], l, mid, k - sum);
    else return Queryk(rson[L], rson[R], mid + 1, r, k);
}

int n, m, A[N];
struct NODE {
    int x, y;
    inline int operator < (const NODE &__) const {
        return x < __.x || (x == __.x && y < __.y);
    }
} node[N];

int stk[N], rem[N], high[N], Top;
int main() {
    n = read<int>();
    rep (i, 1, n) node[i].x = read<int>(), node[i].y = read<int>();
    sort(node + 1, node + n + 1);

    int cur = 1;
    rep (i, 1, n) {
        Rt[i] = Rt[i - 1];
        for ( ; cur <= n && node[cur].x == i; ++cur)
            Insert(Rt[i], Rt[i], 1, n, node[cur].y);
    }

    rep (ks, 1, read<int>()) {
        rep (i, 1, m = read<int>()) A[i] = read<int>();
        sort(A + 1, A + m + 1), Top = 0;
        rep (i, 1, m) {
            for ( ; high[Top] < A[i] && Top; --Top) ;
           	int tot = rem[Top]
                + Query(Rt[stk[Top]], Rt[A[i]], 1, n, A[i]) - A[i];

            if (tot < 0) { puts("0"); break; }
            else if (i == m) { puts("1"); break; }

            int H = Queryk(Rt[stk[Top]], Rt[A[i]], 1, n, tot - rem[Top]);
            for ( ; H > high[Top] && Top; )
                --Top, H = Queryk(Rt[stk[Top]], Rt[A[i]], 1, n, tot - rem[Top]);

            stk[++Top] = A[i], rem[Top] = tot, high[Top] = H;
        }
    }
    return 0;
}

\[on \ 2019.12.15 \]

 posted on 2019-12-15 15:30  Ameiyo  阅读(512)  评论(0编辑  收藏  举报