Search for a command to run...
Progressive hints first, then the full explanation and implementation when you're ready to cash out.
Review status
AI-generated and still unreviewed. Double-check the details before internalizing them.
Hints
Open only as much as you need to keep the solve alive.
Think row-by-row for the first tilt. A vertical bar of height only matters on rows , so each row is split into horizontal segments, and tiles in each segment just compress to the right.
After the right tilt, the down tilt does not care about exact row positions anymore. The final answer for a column is simply: how many rows contain a tile in that column after the right tilt.
Process rows from top to bottom. When the current row is , columns with contain tiles, and bars with are active. As decreases, columns and bars only get added, never removed. That screams DSU/offline.
For a current horizontal segment, suppose its length is and it has active tiles in this row. After tilting right, exactly the suffix of length is occupied. So when a tile column is added to a segment, the occupied suffix grows by cell to the left.
Maintain every DSU segment as an interval with tile count . Its currently occupied columns are . Over many rows, add range increments to the answer using a difference array / Fenwick trick. When two segments merge because a bar disappears, remove both old occupied suffixes and add the merged occupied suffix for the rows during which they existed.
The trick is to stop thinking about individual tiles. Individual tiles are a trap here. This is a tilt puzzle, not a tiny physics simulator. Simulating cells is how your solution goes to the shadow realm.
Row View
Look at one fixed row during the first operation, the right tilt.
A tile exists in column on this row iff
The bar between columns and blocks movement on this row iff
So on row , active bars split the row into independent horizontal segments. Inside one segment, the right tilt is extremely simple: if the segment has tiles, those tiles end up in the rightmost cells of the segment.
After that, the down tilt only stacks each column downward. Therefore the final answer for column is just the number of rows where column is occupied after the right tilt.
So the whole problem becomes:
For every row , split columns by bars with . In each segment, count active tile columns with , then add to the rightmost count columns of that segment.
Still sounds expensive if done row by row. Now we make it offline.
Sweep Rows From Top To Bottom
Process rows .
As decreases:
Why ? Because a bar of height blocks rows . It does not block row . For , the edge is passable even on row .
Use a DSU where each component is a contiguous interval of columns. For every component, store:
L, R: its interval;cnt: how many active tile columns are inside it;last: the highest row from which the component's current state has been valid.For a component with cnt = c, the occupied cells after the right tilt are exactly the suffix
Flushing Contributions
A component's occupied suffix only changes when:
Before changing a component at row , add its current contribution for all rows where it was unchanged.
If its last value is , then the unchanged rows are
so there are rows. Add to every column in its current occupied suffix.
At the end, flush every remaining component down to row , contributing its rows .
Range additions over columns are handled with a simple difference array, because each flush just adds a constant to one interval.
Events
At each row from down to :
Both events start being true on row , so we flush old state to boundary , mutate, and set last = y.
Complexity
Every column activates once. Every edge merges once. Every event does a constant number of DSU operations and one or two range additions.
Total time:
Memory:
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
int main() {
setIO();
int n;
cin >> n;
vector<int> a(n + 1), h(n);
vector<vector<int>> addTile(n + 1), openEdge(n + 1);
for (int i = 1; i <= n; i++) {
cin >> a[i];
if (a[i] > 0) addTile[a[i]].push_back(i);
}
for (int i = 1; i < n; i++) {
cin >> h[i];
openEdge[h[i] + 1].push_back(i);
}
vector<int> parent(n + 1), sz(n + 1, 1), L(n + 1), R(n + 1), cnt(n + 1, 0), last(n + 1, n + 1);
vector<ll> diff(n + 3, 0);
iota(parent.begin(), parent.end(), 0);
for (int i = 1; i <= n; i++) L[i] = R[i] = i;
auto findRoot = [&](int x) {
int r = x;
while (parent[r] != r) r = parent[r];
while (parent[x] != x) {
int p = parent[x];
parent[x] = r;
x = p;
}
return r;
};
auto rangeAdd = [&](int l, int r, ll val) {
if (l > r || val == 0) return;
diff[l] += val;
diff[r + 1] -= val;
};
auto flush = [&](int root, int y) {
root = findRoot(root);
int rows = last[root] - y;
if (rows <= 0 || cnt[root] == 0) return;
int l = R[root] - cnt[root] + 1;
int r = R[root];
rangeAdd(l, r, rows);
};
auto activateTile = [&](int pos, int y) {
int root = findRoot(pos);
flush(root, y);
cnt[root]++;
last[root] = y;
};
auto unite = [&](int edge, int y) {
int aRoot = findRoot(edge);
int bRoot = findRoot(edge + 1);
if (aRoot == bRoot) return;
flush(aRoot, y);
flush(bRoot, y);
if (sz[aRoot] < sz[bRoot]) swap(aRoot, bRoot);
parent[bRoot] = aRoot;
sz[aRoot] += sz[bRoot];
L[aRoot] = min(L[aRoot], L[bRoot]);
R[aRoot] = max(R[aRoot], R[bRoot]);
cnt[aRoot] += cnt[bRoot];
last[aRoot] = y;
};
for (int y = n; y >= 1; y--) {
for (int pos : addTile[y]) activateTile(pos, y);
for (int edge : openEdge[y]) unite(edge, y);
}
for (int i = 1; i <= n; i++) {
if (findRoot(i) == i) flush(i, 0);
}
ll cur = 0;
for (int i = 1; i <= n; i++) {
cur += diff[i];
cout << cur << (i == n ? '\n' : ' ');
}
return 0;
}#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
int main() {
setIO();
int n;
cin >> n;
vector<int> a(n + 1), h(n);
vector<vector<int>> addTile(n + 1), openEdge(n + 1);
for (int i = 1; i <= n; i++) {
cin >> a[i];
if (a[i] > 0) addTile[a[i]].push_back(i);
}
for (int i = 1; i < n; i++) {
cin >> h[i];
openEdge[h[i] + 1].push_back(i);
}
vector<int> parent(n + 1), sz(n + 1, 1), L(n + 1), R(n + 1), cnt(n + 1, 0), last(n + 1, n + 1);
vector<ll> diff(n + 3, 0);
iota(parent.begin(), parent.end(), 0);
for (int i = 1; i <= n; i++) L[i] = R[i] = i;
auto findRoot = [&](int x) {
int r = x;
while (parent[r] != r) r = parent[r];
while (parent[x] != x) {
int p = parent[x];
parent[x] = r;
x = p;
}
return r;
};
auto rangeAdd = [&](int l, int r, ll val) {
if (l > r || val == 0) return;
diff[l] += val;
diff[r + 1] -= val;
};
auto flush = [&](int root, int y) {
root = findRoot(root);
int rows = last[root] - y;
if (rows <= 0 || cnt[root] == 0) return;
int l = R[root] - cnt[root] + 1;
int r = R[root];
rangeAdd(l, r, rows);
};
auto activateTile = [&](int pos, int y) {
int root = findRoot(pos);
flush(root, y);
cnt[root]++;
last[root] = y;
};
auto unite = [&](int edge, int y) {
int aRoot = findRoot(edge);
int bRoot = findRoot(edge + 1);
if (aRoot == bRoot) return;
flush(aRoot, y);
flush(bRoot, y);
if (sz[aRoot] < sz[bRoot]) swap(aRoot, bRoot);
parent[bRoot] = aRoot;
sz[aRoot] += sz[bRoot];
L[aRoot] = min(L[aRoot], L[bRoot]);
R[aRoot] = max(R[aRoot], R[bRoot]);
cnt[aRoot] += cnt[bRoot];
last[aRoot] = y;
};
for (int y = n; y >= 1; y--) {
for (int pos : addTile[y]) activateTile(pos, y);
for (int edge : openEdge[y]) unite(edge, y);
}
for (int i = 1; i <= n; i++) {
if (findRoot(i) == i) flush(i, 0);
}
ll cur = 0;
for (int i = 1; i <= n; i++) {
cur += diff[i];
cout << cur << (i == n ? '\n' : ' ');
}
return 0;
}