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.
All and subgrids are automatic. The only real enemy is a interval, where every path is determined by the column where it moves down.
For a interval, let the path that moves down in column have value . Then So the whole path-value sequence is controlled by the diagonal differences .
The greedy choice at top cell compares exactly those same two cells: right is , down is . So it can move right iff , and it can move down iff .
In an interval, if there is no negative , the greedy path goes all the way right and is optimal. If the first negative is at , greedy can drop at , and this is optimal iff every prefix sum starting at is .
Now enforce that condition globally. After a negative difference, the future prefix sum is not allowed to rise above the height before that negative. Track only the remaining slack : a nonnegative consumes slack, while a negative resets slack to .
The trick is that the grid is mostly a distraction. The useful object is not the full grid, but the diagonal difference sequence That sequence completely decides whether the greedy path can be optimal in every -row subgrid.
Reduce A Subgrid To One Sequence
Consider a subgrid using both rows and columns . A down/right path starts at , moves right on the top row, moves down once, then moves right on the bottom row.
So every path is determined by the column where it moves down. Its value is Compare two neighboring choices: So if , moving the down step one column right improves the path. If , it makes the path worse. If , both are tied.
Now look at the greedy rule. At top cell , the two possible next cells are:
So greedy can move right iff , and can move down iff .
Same comparison. Not similar. Same. That's the whole problem cracking open.
What Makes One Interval Valid?
Inside interval , suppose all differences are nonnegative. Then is nondecreasing, so the best path moves down at . Greedy can move right the whole way, so this interval is fine.
Otherwise, let be the first index in this interval with .
Before , all differences are nonnegative, so Greedy can reach column , and since , it can move down there. Thus the greedy candidate has value .
This greedy path is optimal exactly when no later path beats it: Using the difference sequence, that becomes So every prefix sum starting at the first negative must stay nonpositive.
Because the interval is arbitrary, we can start the interval exactly at any negative position. Therefore the full condition is:
For every with ,
That is both necessary and sufficient.
Turn The Condition Into A Small Automaton
Scan the differences from left to right.
Before we have seen any negative difference, everything is easy: all processed differences are nonnegative, and there is no restriction yet. Call this the free state.
Once we take a negative difference, say from current prefix height using , the future prefix height is never allowed to exceed the old height . Instead of storing absolute heights, store the slack After taking , the current height drops by , so the slack becomes .
From a bounded state with slack :
That reset is the nice part. We do not need prefix sums, caps, or any large coordinate compression circus. Just .
Weights For Each Difference
Difference uses only two cells: These pairs are disjoint for different , which is extremely convenient.
For each position , compute where must match cell if fixed, and must match cell if fixed.
If both are free, then for .
The cells and never appear in any , so they are independent. Multiply the final answer by for each of those cells that is empty.
DP
Let:
free = number of ways where no negative difference has appeared yet,dp[h] = number of ways in bounded mode with current slack .For each position :
A nonnegative difference keeps free free:
A negative difference can be taken from any state and resets slack to :
From bounded slack , a nonnegative is allowed only when :
The answer after all differences is then multiplied by the independent choices for and .
Complexity is per test case, and since and , this is completely fine.
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int MOD = 998244353;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
int main() {
setIO();
int T;
cin >> T;
while (T--) {
int n, k;
cin >> n >> k;
vector<vector<int>> a(2, vector<int>(n));
for (int i = 0; i < 2; i++) {
for (int j = 0; j < n; j++) cin >> a[i][j];
}
int off = k - 1;
ll freeWays = 1;
vector<int> dp(k, 0);
for (int i = 0; i + 1 < n; i++) {
vector<int> w(2 * k - 1, 0);
int fixedX = a[0][i + 1];
int fixedY = a[1][i];
for (int d = -(k - 1); d <= k - 1; d++) {
int cnt = 0;
if (fixedX != -1 && fixedY != -1) {
cnt = (fixedX - fixedY == d);
} else if (fixedX != -1) {
int y = fixedX - d;
cnt = (1 <= y && y <= k);
} else if (fixedY != -1) {
int x = fixedY + d;
cnt = (1 <= x && x <= k);
} else {
cnt = k - abs(d);
}
w[d + off] = cnt;
}
ll boundedSum = 0;
for (int v : dp) boundedSum = (boundedSum + v) % MOD;
ll totalWays = (freeWays + boundedSum) % MOD;
vector<int> ndp(k, 0);
for (int g = 1; g < k; g++) {
ndp[g] = (ndp[g] + totalWays * w[off - g]) % MOD;
}
ll nonnegSum = 0;
for (int d = 0; d < k; d++) nonnegSum += w[off + d];
freeWays = freeWays * (nonnegSum % MOD) % MOD;
for (int h = 0; h < k; h++) {
if (!dp[h]) continue;
for (int d = 0; d <= h; d++) {
ndp[h - d] = (ndp[h - d] + (ll)dp[h] * w[off + d]) % MOD;
}
}
dp.swap(ndp);
}
ll ans = freeWays;
for (int v : dp) ans = (ans + v) % MOD;
if (a[0][0] == -1) ans = ans * k % MOD;
if (a[1][n - 1] == -1) ans = ans * k % MOD;
cout << ans << endl;
}
}#include <bits/stdc++.h>
using namespace std;
using ll = long long;
const int MOD = 998244353;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
int main() {
setIO();
int T;
cin >> T;
while (T--) {
int n, k;
cin >> n >> k;
vector<vector<int>> a(2, vector<int>(n));
for (int i = 0; i < 2; i++) {
for (int j = 0; j < n; j++) cin >> a[i][j];
}
int off = k - 1;
ll freeWays = 1;
vector<int> dp(k, 0);
for (int i = 0; i + 1 < n; i++) {
vector<int> w(2 * k - 1, 0);
int fixedX = a[0][i + 1];
int fixedY = a[1][i];
for (int d = -(k - 1); d <= k - 1; d++) {
int cnt = 0;
if (fixedX != -1 && fixedY != -1) {
cnt = (fixedX - fixedY == d);
} else if (fixedX != -1) {
int y = fixedX - d;
cnt = (1 <= y && y <= k);
} else if (fixedY != -1) {
int x = fixedY + d;
cnt = (1 <= x && x <= k);
} else {
cnt = k - abs(d);
}
w[d + off] = cnt;
}
ll boundedSum = 0;
for (int v : dp) boundedSum = (boundedSum + v) % MOD;
ll totalWays = (freeWays + boundedSum) % MOD;
vector<int> ndp(k, 0);
for (int g = 1; g < k; g++) {
ndp[g] = (ndp[g] + totalWays * w[off - g]) % MOD;
}
ll nonnegSum = 0;
for (int d = 0; d < k; d++) nonnegSum += w[off + d];
freeWays = freeWays * (nonnegSum % MOD) % MOD;
for (int h = 0; h < k; h++) {
if (!dp[h]) continue;
for (int d = 0; d <= h; d++) {
ndp[h - d] = (ndp[h - d] + (ll)dp[h] * w[off + d]) % MOD;
}
}
dp.swap(ndp);
}
ll ans = freeWays;
for (int v : dp) ans = (ans + v) % MOD;
if (a[0][0] == -1) ans = ans * k % MOD;
if (a[1][n - 1] == -1) ans = ans * k % MOD;
cout << ans << endl;
}
}