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;
}