题解 P4352【[CERC2015] Greenhouse Growth】/ SS241030D【生长】
题目描述
你在潇杻国一座城市的街道上,这条街道是一个很长的直线。
在这条街道上,从左到右依次排布着 \(n\) 棵树。开始时,第 \(i\) 棵树的高度为 \(h_i\)。
因为人在挫折中成长,所以树在风暴中成长。有 \(m\) 场风暴依次降临,每场风暴会从左向右或从右向左依次席卷这条街道上的每一颗树。当第 \(i\) 棵树被风暴席卷时,如果前一棵被风暴席卷的树比第 \(i\) 棵树高,那么第 \(i\) 棵树会长高 \(1\) 个单位的高度。最开始被席卷的树的高度不会有任何变化。
每场风暴会席卷每棵树恰好一次,每棵树是否长高取决于其前一棵树被本场风暴席卷后的高度。
由于潇杻国国王柬鋀只会做计数题,所以你需要帮他求出 \(m\) 场风暴结束后每一棵树的高度。
从左向右的风暴叫作 A 风,从右向左的风暴叫作 B 风。
对于所有测试数据,\(2 \leq n,m \leq 3\times 10^5,1\leq h_i\leq 10^9\)。
特殊性质 A:\(h_i\neq h_{i-1}\) 的 \(i\) 只有 \(100\) 个。
特殊性质 B:风暴全是 A 风。
题解
特殊性质 A:连续的高度相同的树会被同时拔高或者不拔高。所以将连续段并起来就做完了。
特殊性质 B:假设 \(i-1\) 这个位置最终变成 \(a_{i-1}\),现在输入 \(a_i\),有几种情况:
- \(a_{i-1}\leq a_i\),风暴无法撼动这棵树。
- \(a_{i-1}\leq a_i+m\),这时候这棵树只能长高到 \(a_{i-1}\),这一定做得到。
- \(a_{i-1}>a_i+m\),这棵树只能长高到 \(a_i+m\)。
以上三个讨论覆盖所有情况,因此可以 \(O(1)\) 计算 \(i\) 位置最终的高度,然后 \(O(n)\) 计算整个序列的答案。
正解的第一步肯定是将连续段并起来,并观察到连续段只会被并 \(O(n)\) 次所以很可能要暴力维护连续段。发现连续段之间的差在归零之前每次要么 \(-1\) 要么不变且只取决于风向(以及旁边的连续段不被合并)。
仔细刻画连续段之间的差到底长啥样。记 \(ca(i)\) 表示吹 A 风时 \(i\) 是否会升高,\(cb(i)\) 表示吹 B 风时 \(i\) 是否会升高。现在 \(i, j\) 两个连续段是相邻的,记 \(a=[ca(i)\neq ca(j)], b=[cb(i)\neq cb(j)]\),则从当前时刻出发,经过 \(ta\) 个 A 风和 \(tb\) 个 B 风后 \(i, j\) 之间的差降低 \(a\cdot ta+b\cdot tb\),这个在 \(a, b\) 改变之前都适用,而且这样可以轻易计算出这个连续段在没有意外的时候什么时候归零(记为 \(tim\),可能需要处理出某时刻之前有多少个 A/B 风以及第 \(i\) 个 A/B 风在什么时刻上)。每次合并连续段的时候,则将旁边 \(O(1)\) 个连续段的差先结算再修改它们的 \(a, b\)。
由此导出一个恐怖的做法:维护每对相邻连续段的 \(a, b, tim, lst, hei\) 其中 \(hei\) 是考虑了 \([1, lst]\) 中的操作后的差,\(a, b, tim\) 同上。计算出 \(tim\) 之后,对每个操作开一个桶,将这对连续段放入第 \(tim\) 个桶,当从前往后扫到这个操作时就可以将这两个连续段合并起来。事实上一直没有说的是外面需要维护一个双向链表和并查集,例如双向链表有 \(L, R\) 表示前驱后继,然后相邻连续段的信息存在后一个连续段上,当前在 \(t\) 操作尝试合并 \(i, L[i]\),则你可能需要先结算 \(R[i]\) 至 \(t\) 时刻(这里是因为需要正确维护 \(R[i]\) 的 \(hei\)),然后删去 \(i\),将 \(i\) 往 \(L[i]\) 上合并,然后分别结算并更新 \(L[L[i]], L[i], R[i], R[R[i]]\) 对应的一大坨东西。总复杂度是 \(O(n+m)\),注意并查集先 merge 再 find 复杂度仍是线性,注意仔细实现小心同一个点被删两次导致桶大小超级加倍或者迭代器原地失效等逆天错误。
code
没删调试,自己删吧
#include <bits/stdc++.h>
using namespace std;
#ifdef LOCAL
#define debug(...) fprintf(stderr, ##__VA_ARGS__)
#else
#define endl "\n"
#define debug(...) void(0)
#endif
using LL = long long;
template <int N>
struct dsu {
int fa[N + 10], siz[N + 10];
void init(int n) { for (int i = 1; i <= n; i++) fa[i] = i, siz[i] = 1; }
int find(int x) { return x == fa[x] ? x : fa[x] = find(fa[x]); }
bool merge(int x, int y) {
x = find(x), y = find(y);
if (x == y) return false;
// if (siz[x] < siz[y]) swap(x, y);
siz[x] += siz[y], fa[y] = x;
return true;
}
};
int n, m, a[300010], L[300010], R[300010], op[300010], oid[2][300010], b[300010];
int ops[2][300010], cnt[2];
dsu<300010> dsy;
int ca(int i) { return L[i] >= 1 && a[i] < 0; }
int cb(int i) { return R[i] <= n && a[R[i]] > 0; }
vector<int> vec[300010];
int arga[300010], argb[300010], lst[300010], hei[300010], tim[300010];
int vis[300010];
void update(int i, int t) {
if (L[i] < 1 || i > n) return ;
assert(lst[i] <= t);
// i & L[i]
// (lst, t)
if (lst[i] < t) {
hei[i] -= arga[i] * (oid[0][t] - oid[0][max(lst[i], 0)]);
hei[i] -= argb[i] * (oid[1][t] - oid[1][max(lst[i], 0)]);
}
// without t
arga[i] = ca(i) != ca(L[i]);
argb[i] = cb(i) != cb(L[i]);
lst[i] = t;
if (arga[i] && argb[i]) tim[i] = t + hei[i];
else if (arga[i]) tim[i] = ops[0][min(m + 1, oid[0][t] + hei[i])];
else if (argb[i]) tim[i] = ops[1][min(m + 1, oid[1][t] + hei[i])];
else tim[i] = m + 1;
tim[i] = min(tim[i], m + 1);
if (tim[i] != t && tim[i] <= m) vec[tim[i]].push_back(i);
debug("update %d -> tim = %d\n", i, tim[i]);
}
int main() {
#ifndef LOCAL
#ifndef NF
freopen("growing.in", "r", stdin);
freopen("growing.out", "w", stdout);
#endif
cin.tie(nullptr)->sync_with_stdio(false);
#endif
cin >> n >> m;
for (int i = 1; i <= n; i++) cin >> a[i];
dsy.init(n);
memset(ops, 0x3f, sizeof ops);
for (int i = 1; i <= m; i++) {
char _op;
cin >> _op;
op[i] = _op != 'A';
oid[op[i]][i] = oid[op[i]][i - 1] + 1;
oid[!op[i]][i] = oid[!op[i]][i - 1];
ops[op[i]][++cnt[op[i]]] = i;
}
for (int i = n; i >= 1; i--) a[i] -= a[i - 1], hei[i] = abs(a[i]), debug("hei[%d] = %d\n", i, hei[i]);
for (int i = 1; i <= n; i++) L[i] = i - 1, R[i] = i + 1;
L[0] = 0, R[0] = 1, L[n + 1] = n, R[n + 1] = n + 1;
for (int i = R[R[0]]; i <= n; i = R[i]) if (!hei[i]) dsy.merge(L[i], i), L[R[i]] = L[i], R[L[i]] = R[i], debug("rm %d\n", i);
for (int i = R[R[0]]; i <= n; i = R[i]) update(i, 0);
#ifdef LOCAL
debug("after t = %d\n", 0);
for (int i = R[R[0]]; i <= n; i = R[i]) debug("st = %d, args = {%d, %d}, lst = %d, hei = %d, tim = %d\n", i, arga[i], argb[i], lst[i], hei[i], tim[i]);
#endif
for (int t = 1; t <= m; t++) {
if (R[1] <= n && a[R[1]] > 0 && op[t]) hei[1] += 1;
vector<int> sb;
for (int i : vec[t]) if (tim[i] == t)
sb.push_back(i); for (int i : sb)
if (vis[i] < t) {
update(R[i], t);
// if (R[i] <= n && a[R[i]] > 0 && a[i] < 0) hei[R[i]] -= 1, debug("i = %d\n", i);
dsy.merge(L[i], i), L[R[i]] = L[i], R[L[i]] = R[i];
update(L[i], t), update(L[L[i]], t), update(R[i], t), update(R[R[i]], t);
vis[i] = t;
tim[i] = m + 1;
}
#ifdef LOCAL
debug("after t = %d\n", t);
for (int i = R[R[0]]; i <= n; i = R[i]) debug("st = %d, args = {%d, %d}, lst = %d, hei = %d, tim = %d\n", i, arga[i], argb[i], lst[i], hei[i], tim[i]);
#endif
}
assert(R[0] == 1);
b[1] = hei[1];
debug("b[1] = %d\n", b[1]);
for (int i = R[1]; i <= n; i = R[i]) update(i, m), b[i] = b[L[i]] + (a[i] < 0 ? -hei[i] : hei[i]);
#ifdef LOCAL
debug("after t = %d\n", m + 1);
for (int i = R[R[0]]; i <= n; i = R[i]) debug("st = %d, args = {%d, %d}, lst = %d, hei = %d, tim = %d\n", i, arga[i], argb[i], lst[i], hei[i], tim[i]);
#endif
for (int i = 1; i <= n; i++) debug("dsy.find(%d) = %d\n", i, dsy.find(i));
for (int i = 1; i <= n; i++) cout << b[dsy.find(i)] << " \n"[i == n];
return 0;
}
本文来自博客园,作者:caijianhong,转载请注明原文链接:https://www.cnblogs.com/caijianhong/p/18516665/solution-SS241030D