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.
A cut is the same as choosing how many cells are taken from the left of each row. If row takes cells, then the path condition means
If the chosen side has ones and the whole table has ones, the product is . So the dream is to make as close as possible to .
You never need a fancy-shaped cut to prove reachability. Consider cuts where all rows above some row take cells, all rows below it take all cells, and only one row is partially taken.
Scan rows from bottom to top. The number of ones in the chosen side increases by whole row sums. When the target falls inside a row, take just enough prefix of that row.
Inside one binary row, every value from to that row's number of ones is achievable by some prefix, because the prefix-one count only increases by at a time. That gives an exact half-ish split, then the path is just built from the resulting nondecreasing prefix lengths.
Let be the total number of ones in the table. If one part contains ones, the other contains , so the value is
This parabola is maximized when is as close as possible to . Therefore the best possible answer is
The real question is whether we can always construct a valid cut with exactly
ones on one side. Yep. The table looks scary, but the construction is honestly pretty blunt.
Describe a cut by values , where is the number of cells taken from the left side of row . A valid monotone path requires
Now use a very restricted shape:
This is valid because the sequence looks like
which is nondecreasing. No weird DP needed. Nice.
Scan rows from bottom to top while counting how many ones are already included from fully taken lower rows. Suppose this count is below.
For the current row:
below + rowOnes < k, then even taking the whole row is not enough, so set this row to full and continue upward;ones from a prefix of the current row.
Because the row contains only and , its prefix-one count starts at and increases by exactly whenever we pass a one. So every value from to rowOnes appears as some prefix count. Pick the first column where the prefix has exactly need ones.
Then set:
That chosen side has exactly ones, so the product is optimal.
Finally, convert the prefix lengths into the output path. Start at column . For each row , move right until the current column equals , then move down once. After all rows, move right until column .
Since the are nondecreasing, this never asks the path to move left. The output has exactly D moves and R moves.
Edge cases are clean:
The algorithm reads the table once, scans rows once, and builds a path of length . Complexity is
per test case, with memory for storing the table. This fits because the total number of cells over all test cases is at most .
#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 t;
cin >> t;
while (t--) {
int n, m;
cin >> n >> m;
vector<vector<int>> a(n, vector<int>(m));
vector<int> row_sum(n, 0);
int total = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
cin >> a[i][j];
row_sum[i] += a[i][j];
}
total += row_sum[i];
}
int target = total / 2;
vector<int> take(n, 0);
int below = 0;
for (int i = n - 1; i >= 0; i--) {
if (below + row_sum[i] < target) {
take[i] = m;
below += row_sum[i];
continue;
}
int need = target - below;
int pref = 0;
int col = 0;
if (need > 0) {
for (int j = 0; j < m; j++) {
pref += a[i][j];
if (pref == need) {
col = j + 1;
break;
}
}
}
take[i] = col;
break;
}
ll best = 1LL * target * (total - target);
string path;
int cur_col = 0;
for (int i = 0; i < n; i++) {
while (cur_col < take[i]) {
path.push_back('R');
cur_col++;
}
path.push_back('D');
}
while (cur_col < m) {
path.push_back('R');
cur_col++;
}
cout << best << '\n' << path << '\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 t;
cin >> t;
while (t--) {
int n, m;
cin >> n >> m;
vector<vector<int>> a(n, vector<int>(m));
vector<int> row_sum(n, 0);
int total = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
cin >> a[i][j];
row_sum[i] += a[i][j];
}
total += row_sum[i];
}
int target = total / 2;
vector<int> take(n, 0);
int below = 0;
for (int i = n - 1; i >= 0; i--) {
if (below + row_sum[i] < target) {
take[i] = m;
below += row_sum[i];
continue;
}
int need = target - below;
int pref = 0;
int col = 0;
if (need > 0) {
for (int j = 0; j < m; j++) {
pref += a[i][j];
if (pref == need) {
col = j + 1;
break;
}
}
}
take[i] = col;
break;
}
ll best = 1LL * target * (total - target);
string path;
int cur_col = 0;
for (int i = 0; i < n; i++) {
while (cur_col < take[i]) {
path.push_back('R');
cur_col++;
}
path.push_back('D');
}
while (cur_col < m) {
path.push_back('R');
cur_col++;
}
cout << best << '\n' << path << '\n';
}
return 0;
}