zoukankan      html  css  js  c++  java
  • 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 ]

  • 相关阅读:
    配置类Configuration怎样使用
    MVC三和,你能辨别它?
    杭州电 1372 Knight Moves(全站搜索模板称号)
    Verilog之i2c合约
    android requestDisallowInterceptTouchEvent用途
    其简单的通用适配器的定义
    开发专题指南: JEECG高速微云开发平台前言
    (1)QlikView概要
    HDU2841 Visible Trees (容斥原理)
    每天进步一点点——Linux系统时间来处理
  • 原文地址:https://www.cnblogs.com/Ameiyo/p/12044307.html
Copyright © 2011-2022 走看看