• IOI 2015 Teams 分组


    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 ]

  • 相关阅读:
    Allegro PCB Design GXL (legacy) 使用slide无法将走线推挤到焊盘的原因
    OrCAD Capture CIS 16.6 导出BOM
    Altium Designer (17.0) 打印输出指定的层
    Allegro PCB Design GXL (legacy) 将指定的层导出为DXF
    Allegro PCB Design GXL (legacy) 设置十字大光标
    Allegro PCB Design GXL (legacy) 手动更改元器件引脚的网络
    magento产品导入时需要注意的事项
    magento url rewrite
    验证台湾同胞身份证信息
    IE8对css文件的限制
  • 原文地址:https://www.cnblogs.com/Ameiyo/p/12044307.html
Copyright © 2020-2023  润新知