「题解」洛谷 P3586 [POI2015]LOG

题目

P3586 [POI2015]LOG

简化题意

维护一个序列(一开始都是 (0))可以单点修改,支持询问能不进行 (s) 次每次挑 (x) 个正数,并减 (1)(并不是真正的减)的操作。

思路

考虑每个数在一次询问中的贡献

  • 一个数如果大于 (s) 那么它能够被选 (s) 次。
  • 一个数如果小于 (s) 那么它能够被选它的大小次。

所以只需要判断 (largesumlimits_{i = 1}^{n} min(s,a[i])) 是否大于 (s imes x) 就行。

考虑去维护小于 (s) 的数目以及他们的和。

开两个树状数组,下标都是离散化过的,一个存离散化过后下标这么大的数出现过几次,一个存离散化过后下标这么大数在离散化之前的权值。

感觉维护有点麻烦所以在代码中详细写了下注释

Code

#include <cstdio>
#include <cstring>
#include <string>
#include <iostream>
#include <algorithm>
#define MAXN 1000001
#define int long long

int n, m, num0, a[MAXN], lsh1[MAXN], lsh2[MAXN];
//a 是离散化之前的序列.
//lsh2 是离散化之后的序列.
//lsh1 是对题目询问中权值离散化.
struct query {
    int opt, x, y;
}q[MAXN];
struct lsh {
    int w, id;
    friend bool operator < (lsh l1, lsh l2) {
        return l1.w < l2.w;
    }
}c[MAXN];
struct BIT {
    int c[MAXN];
    int lowbit(int x) { return x & (-x); }
    void add(int x, int k) {
        while (x <= m) {
            c[x] += k;
            x += lowbit(x);
        }
    }
    int sum(int x) {
        int ans = 0;
        while (x) {
            ans += c[x];
            x -= lowbit(x);
        }
        return ans;
    }
}b[2];
//b[0] 离散化后个数
//b[1] 离散化前数量 

signed main() {
    scanf("%lld %lld", &n, &m), num0 = n;
    char opt;
    for (int i = 1; i <= m; ++i) {
        while (opt != 'U' && opt != 'Z') opt = getchar();
        scanf("%lld %lld", &q[i].x, &q[i].y);
        q[i].opt = (opt == 'U' ? 1 : 0);
        c[i].w = q[i].y, c[i].id = i;
        opt = getchar();
    }
    std::sort(c + 1, c + m + 1);
    for (int i = 1, now = 0; i <= m; ++i) {
        if (c[i].w != c[i - 1].w) ++now;
        lsh1[c[i].id] = now;
    }//离散化 
    for (int i = 1; i <= m; ++i) {
        if (q[i].opt) {
            if (lsh2[q[i].x] != 0) b[0].add(lsh2[q[i].x], -1);
            //如果离散化后的序列中这个元素不是 0 就给它的出现次数减 1.
            else if (lsh2[q[i].x] == 0 && lsh1[i] != 0) --num0;
            //更改序列中 0 的数目.
            if (lsh1[i] != 0) b[0].add(lsh1[i], 1);
            //如果离散化后的更改操作不是 0 就给它的出现次数加 1.
            else if (lsh2[q[i].x] != 0) ++num0;
            //更改序列中 0 的数目.
            if (lsh2[q[i].x] != 0) b[1].add(lsh2[q[i].x], -a[q[i].x]);
            //更改大小.
            if (lsh1[i] != 0) b[1].add(lsh1[i], q[i].y);
            //更改大小.
            a[q[i].x] = q[i].y, lsh2[q[i].x] = lsh1[i];
            //更改离散化前后原序列中的值.
        }
        else {
            int bz1 = b[1].sum(lsh1[i]);//小于等于 s 的数的和
            int bz2 = n - b[0].sum(lsh1[i]) - num0;//大于 s 的数的个数。 
            if (bz1 + bz2 * q[i].y >= q[i].x * q[i].y) puts("TAK");
            else puts("NIE");
        }
    }
    return 0;
}
原文地址:https://www.cnblogs.com/poi-bolg-poi/p/13598188.html