P3586 POI2015 Logistyka

P3586 POI2015 LOG - 洛谷 | 计算机科学教育新生态 (luogu.com.cn)

先简单 hack 一下操作二和 \(\sum a \ge cs\) 等价这种错解:\(a = (1, 1, 4)\) 无法完成 \(c = 3, s = 2\)

运用P5815 CQOI2010 扑克牌的思想,我们可以将每轮操作二看成:

第一步:给 \(a\) 中不同的 \(n - c\) 个数 \(+ 1\);第二步:给 \(a\) 中所有的数 \(-1\),判断是否保证每轮是否都够减。

然后分解,考虑先进行 \(s\) 轮第一步,再进行 \(s\) 轮第二步(因为第二步的操作永远是固定的)。

就成功把操作二转化为,能否操作 \(s\) 轮,每轮可以使 \(a\) 中不同的 \(n - c\) 个数 \(+1\)。最终让每个数都达到 \(s\)

容易想到,每轮我们选择 \(a\) 中最小的 \(n - c\) 个将它们 \(+1\) 是非常优秀的。

考虑证明,设 \(p = \sum\limits_{a_i \le s}s - a_i\),其含义为每个小于等于 \(s\) 的数补到 \(s\) 需要被 \(+1\) 的次数总和。

显然 $p > s(n - c) $ 时一定无解。

\(p \le s(n - c)\) 时,初始的 \(0\) 的数量一定不超过 \(n - c\),因此我们第一轮选数一定会把所有的 \(0\) 选上并把它们 \(+1\),第一轮后 \(a\) 的最小值一定大于等于 \(1\)。而第一轮后 \(a\)\(1\) 的数量一定也不超过 \(n - c\),否则同样和 \(p \le s(n - c)\) 矛盾,所以第二轮选数会选中所有的 \(1\),第二轮后 \(a\) 的最小值一定大于等于 \(2\)……因此第 \(s\) 轮后 \(a\) 的最小值一定大于等于 \(s\)

更严谨的说明

因此操作二和判断 \(p \le s(n - c)\) 等价。其中,\(c, s, n\) 都是已知的参数,\(p\) 就是我们维护的内容。


因此原问题可以简化成:维护一个支持单点修改的序列,支持查询 \(p = \sum\limits_{a_i \le s}s - a_i\)

思考查询内容的本质:小于等于 \(s\)\(a_i\) 中,\(s - a_i\) 的总和。容易发现,设 \(f\) 是小于 \(s\) 的数的数量,\(g\) 是小于 \(s\) 的数的和,\(p\) 可以改写为 \(fs - g\)。而 \(f\)\(g\) 可以用两个权值树状数组分别快速维护。

具体来说,\(t[x]\) 存储值为 \(x\)\(a_i\) 的数量,\(t2[x]\) 存储值为 \(x\)\(a_i\) 的总和,那么 \(f\) 可以通过查询 \(t\)\([1, s]\) 的前缀和得到,\(g\) 可以通过查询 \(t2\)\([1, s]\) 的前缀和得到。最后,\(t1, t2\) 使用下标离散化和树状数组优化加速。

时间复杂度 \(\mathcal{O}(m \log m + m \log n)\),需要离线(离散化需要)。


bonus:操作二的另一种处理思路,最后得到的式子是一样的。

/*
 * @Author: crab-in-the-northeast 
 * @Date: 2022-10-25 10:17:19 
 * @Last Modified by: crab-in-the-northeast
 * @Last Modified time: 2022-10-25 18:00:46
 */
#include <bits/stdc++.h>
#define int long long
inline int read() {
    int x = 0;
    bool flag = true;
    char ch = getchar();
    while (!isdigit(ch)) {
        if (ch == '-')
            flag = false;
        ch = getchar();
    }
    while (isdigit(ch)) {
        x = (x << 1) + (x << 3) + ch - '0';
        ch = getchar();
    }
    if(flag)
        return x;
    return ~(x - 1);
}
inline char rech() {
    char ch = getchar();
    while (!isalpha(ch))
        ch = getchar();
    return ch;
}

inline int lowbit(int x) {
    return x & (-x);
}

const int maxn = (int)1e6 + 5;
const int maxm = (int)1e6 + 5;

int typ[maxm], op1[maxm], op2[maxm];

int ua[maxn];

struct fenwick {
    int c[maxn], n;

    inline void add(int x, int v) {
        for (; x <= n; x += lowbit(x))
            c[x] += v;
    }

    inline int get(int x) {
        int ans = 0;
        for (; x; x -= lowbit(x))
            ans += c[x];
        return ans;
    }
} t1, t2;

int a[maxn];

int *en;

inline int lsh(int x) {
    return std :: lower_bound(ua + 1, en, x) - ua;
}

signed main() {
    int n = read(), m = read();

    ua[1] = 0;
    for (int i = 1; i <= m; ++i) {
        char ch = rech();
        typ[i] = (ch == 'U' ? 1 : 2);
        op1[i] = read();
        op2[i] = read();
        ua[i + 1] = op2[i];
    }

    std :: sort(ua + 1, ua + m + 2);
    en = std :: unique(ua + 1, ua + m + 2);

    t1.n = t2.n = m + 1;
    t1.add(lsh(0), n);
    for (int i = 1; i <= m; ++i) {
        if (typ[i] == 1) {
            int x = op1[i], v = op2[i];
            int lax = lsh(a[x]), lv = lsh(v);
            t1.add(lax, -1);
            t2.add(lax, -a[x]);
            a[x] = v;
            t1.add(lv, 1);
            t2.add(lv, v);
        } else {
            int c = op1[i], s = op2[i], ls = lsh(s);
            int f = t1.get(ls), g = t2.get(ls);
            puts(f * s - g <= s * (n - c) ? "TAK" : "NIE");
        }
    }

    return 0;
}
posted @ 2022-10-25 10:42  dbxxx  阅读(62)  评论(0编辑  收藏  举报