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.
Do not try to count orientations path-by-path first. Treat every corner direction as uniformly random, compute the probability of a bad orientation, then multiply by at the end.
The car only ever moves down or right. A valid orientation is exactly the complement of the car exiting through the bottom or right border.
A plain is missing information. When the car enters a cell, one nearby corner has already been partially revealed, and that corner can block the next move.
Use four states: entered from above and the top-right corner is down / not down, or entered from the left and the bottom-left corner is right / not right.
Set outside states to bad probability . For each cell, enumerate the two still-random corners that affect the next move. The start is a mix of the two entered-from-above states.
Turn the count into a probability
There are orientations, all equally likely. So instead of counting valid orientations directly, compute
= probability that the car exits the grid.
Then
.
This is the clean way in. Direct counting gets nasty fast; probability lets us average over unrevealed wall directions locally.
Which walls matter
For a current cell:
The car tries down first. Only if down is blocked does it care about the right edge.
Why coordinates alone are not enough
Suppose the car enters a cell from above. The top edge had to be open, so the top-right corner is known not to point left. But whether it points down still matters, because that same corner can block the right edge.
Suppose the car enters from the left. Similarly, whether the bottom-left corner points right matters, because that can block the bottom edge.
So each state needs one bit of memory. That is the whole trick; miss this and the DP just lies to your face.
Let be the number of rows remaining including the current cell, and be the number of columns remaining including the current cell. Define bad-exit probabilities:
If or , the car is already outside, so all four values are .
Deriving the transitions
First take . The top-right corner points down, so the right edge is already blocked. The car can avoid stopping only by moving down. The bottom edge is open with probability . Given it is open, the bottom-right corner is not left; among its three allowed directions, one is down and two are not. Therefore
.
For , the down move contributes the same first two terms. If down is blocked but right is open, the car moves right. Enumerating the bottom-left and bottom-right corners gives probability that the new bottom-left points right, and that it does not. So
.
For , the bottom-left corner points right, so the bottom edge is already blocked. The car can only move right. The right edge is open if the top-right is not down and the bottom-right is not up. If the bottom-right is right, the new state is ; if it is down or left, the new state is :
.
For , the bottom-left corner does not point right. The car moves down if the bottom-right corner is not left. If that bottom-right corner is down, the next state is ; if it is up or right, the next state is . If the bottom-right is left, down is blocked, and then right is open with probability , producing :
.
The missing probability mass in these formulas is not forgotten. It is exactly the case where the car stops inside the grid, which contributes to bad-exit probability.
Starting state
At the top-left cell, pretend the car entered from above. The top-right corner is completely random: it points down with probability , and not down with probability . Hence
.
Finally multiply the good probability by the total number of orientations:
.
All fractions are computed modulo using modular inverses. The DP only depends on the previous row and the current row, so memory is . Time is for the largest requested rectangle, which is fine for . Tiny DP tax, huge payoff.
#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
const ll MOD = 1000000007LL;
ll modPow(ll a, long long e) {
ll res = 1;
while (e > 0) {
if (e & 1) res = res * a % MOD;
a = a * a % MOD;
e >>= 1;
}
return res;
}
int main() {
setIO();
const ll inv2 = (MOD + 1) / 2;
const ll inv4 = inv2 * inv2 % MOD;
const ll inv8 = inv4 * inv2 % MOD;
const ll inv16 = inv8 * inv2 % MOD;
const ll c3_16 = 3 * inv16 % MOD;
const ll c3_8 = 3 * inv8 % MOD;
const ll c5_16 = 5 * inv16 % MOD;
const ll c3_4 = 3 * inv4 % MOD;
int t;
cin >> t;
vector<pair<int, int>> q(t);
int maxN = 0, maxM = 0;
for (int id = 0; id < t; id++) {
cin >> q[id].first >> q[id].second;
maxN = max(maxN, q[id].first);
maxM = max(maxM, q[id].second);
}
vector<vector<pair<int, int>>> byRows(maxN + 1);
for (int id = 0; id < t; id++) {
byRows[q[id].first].push_back({q[id].second, id});
}
vector<ll> ans(t);
vector<array<ll, 4>> prev(maxM + 1), cur(maxM + 1);
for (int j = 0; j <= maxM; j++) prev[j] = {1, 1, 1, 1};
for (int rows = 1; rows <= maxN; rows++) {
cur[0] = {1, 1, 1, 1};
for (int cols = 1; cols <= maxM; cols++) {
ll a0 = (c3_16 * prev[cols][0] + c3_8 * prev[cols][1]) % MOD;
ll a1 = (a0 + inv16 * cur[cols - 1][2] + c5_16 * cur[cols - 1][3]) % MOD;
ll l0 = (c3_16 * cur[cols - 1][2] + c3_8 * cur[cols - 1][3]) % MOD;
ll l1 = (inv4 * prev[cols][0] + inv2 * prev[cols][1] + c3_16 * cur[cols - 1][3]) % MOD;
cur[cols] = {a0, a1, l0, l1};
}
for (auto [cols, id] : byRows[rows]) {
ll bad = (inv4 * cur[cols][0] + c3_4 * cur[cols][1]) % MOD;
ll good = (1 - bad + MOD) % MOD;
ll total = modPow(4, 1LL * (rows + 1) * (cols + 1));
ans[id] = good * total % MOD;
}
swap(prev, cur);
}
for (int id = 0; id < t; id++) {
cout << ans[id] << '\n';
}
}#include <bits/stdc++.h>
using namespace std;
using ll = long long;
void setIO() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
}
const ll MOD = 1000000007LL;
ll modPow(ll a, long long e) {
ll res = 1;
while (e > 0) {
if (e & 1) res = res * a % MOD;
a = a * a % MOD;
e >>= 1;
}
return res;
}
int main() {
setIO();
const ll inv2 = (MOD + 1) / 2;
const ll inv4 = inv2 * inv2 % MOD;
const ll inv8 = inv4 * inv2 % MOD;
const ll inv16 = inv8 * inv2 % MOD;
const ll c3_16 = 3 * inv16 % MOD;
const ll c3_8 = 3 * inv8 % MOD;
const ll c5_16 = 5 * inv16 % MOD;
const ll c3_4 = 3 * inv4 % MOD;
int t;
cin >> t;
vector<pair<int, int>> q(t);
int maxN = 0, maxM = 0;
for (int id = 0; id < t; id++) {
cin >> q[id].first >> q[id].second;
maxN = max(maxN, q[id].first);
maxM = max(maxM, q[id].second);
}
vector<vector<pair<int, int>>> byRows(maxN + 1);
for (int id = 0; id < t; id++) {
byRows[q[id].first].push_back({q[id].second, id});
}
vector<ll> ans(t);
vector<array<ll, 4>> prev(maxM + 1), cur(maxM + 1);
for (int j = 0; j <= maxM; j++) prev[j] = {1, 1, 1, 1};
for (int rows = 1; rows <= maxN; rows++) {
cur[0] = {1, 1, 1, 1};
for (int cols = 1; cols <= maxM; cols++) {
ll a0 = (c3_16 * prev[cols][0] + c3_8 * prev[cols][1]) % MOD;
ll a1 = (a0 + inv16 * cur[cols - 1][2] + c5_16 * cur[cols - 1][3]) % MOD;
ll l0 = (c3_16 * cur[cols - 1][2] + c3_8 * cur[cols - 1][3]) % MOD;
ll l1 = (inv4 * prev[cols][0] + inv2 * prev[cols][1] + c3_16 * cur[cols - 1][3]) % MOD;
cur[cols] = {a0, a1, l0, l1};
}
for (auto [cols, id] : byRows[rows]) {
ll bad = (inv4 * cur[cols][0] + c3_4 * cur[cols][1]) % MOD;
ll good = (1 - bad + MOD) % MOD;
ll total = modPow(4, 1LL * (rows + 1) * (cols + 1));
ans[id] = good * total % MOD;
}
swap(prev, cur);
}
for (int id = 0; id < t; id++) {
cout << ans[id] << '\n';
}
}