设 \(f(i, x, y)\) 表示考虑前 \(i\) 维, 当前和 \(P\) 的曼哈顿距离为 \(x\), 和 \(Q\) 的曼哈顿距离为 \(y\) 的方案数.
则 \(f(i, x, y) = \sum _ {s = -2000} ^ {2000} f(i - 1, x - |s - p _ i|, y - |s - q _ i|)\).
按照 \(s < \min(p _ i, q _ i)\), \(s > \max(p _ i, q _ i)\), \(\min(p _ i, q _ i) \leq s \leq \max(p _ i, q _ i)\) 三分类, 每部分分别做前缀和优化即可. 时间复杂度 \(\mathcal{O}(n V ^ 2)\).
void solve(){ int n, d; cin >> n >> d; vector<int> p(n + 1); for(int i = 1; i <= n; i ++) cin >> p[i]; vector<int> q(n + 1); for(int i = 1; i <= n; i ++) cin >> q[i]; vector<vector<vector<modint>>> dp(n + 1, vector<vector<modint>>(d + 1, vector<modint>(d + 1))); dp[0][0][0] = modint(1); for(int i = 1; i <= n; i ++){ vector<vector<modint>> sumX(d + 1, vector<modint>(d + 1)); for(int x = 0; x <= d; x ++) for(int y = 0; y <= d; y ++){ if(x && y) sumX[x][y] = sumX[x - 1][y - 1]; int s = min(p[i], q[i]) - 1; if(p[i] - s <= x && q[i] - s <= y) sumX[x][y] += dp[i - 1][x - (p[i] - s)][y - (q[i] - s)]; } vector<vector<modint>> sumY(d + 1, vector<modint>(d + 1)); for(int x = 0; x <= d; x ++) for(int y = 0; y <= d; y ++){ if(x && y) sumY[x][y] = sumY[x - 1][y - 1]; int s = max(p[i], q[i]) + 1; if(s - p[i] <= x && s - q[i] <= y) sumY[x][y] += dp[i - 1][x - (s - p[i])][y - (s - q[i])]; } vector<vector<modint>> sumZ(d + 1, vector<modint>(2 * d + 1)); int P = min(p[i], q[i]), Q = max(p[i], q[i]); for(int x = 0; x <= d; x ++) for(int y = 0; y <= 2 * d; y ++){ if(x && y < 2 * d) sumZ[x][y] = sumZ[x - 1][y + 1]; if(y - (Q - P) >= 0 && y - (Q - P) <= d) sumZ[x][y] += dp[i - 1][x][y - (Q - P)]; if(x - (Q - P) - 1 >= 0 && y < d) sumZ[x][y] -= dp[i - 1][x - (Q - P) - 1][y + 1]; } for(int x = 0; x <= d; x ++) for(int y = 0; y <= d; y ++) dp[i][x][y] = sumX[x][y] + sumY[x][y] + sumZ[x][y]; } modint ans(0); for(int x = 0; x <= d; x ++) for(int y = 0; y <= d; y ++) ans += dp[n][x][y]; cout << ans.val() << "\n"; }
线段树维护区间 \(0, 1, 2\) 的个数和 \(01, 02, 10, 12, 20, 21\) 的个数即可.
时间复杂度 \(\mathcal{O}(n + q \log n)\).
struct Sgt{ private: #define ls(o) 2 * o #define rs(o) 2 * o + 1 struct Item{ array<int, 3> once; array<array<ll, 3>, 3> twice; } ; vector<Item> tree; vector<array<int, 3>> lazy; int n; Item merge(Item ls, Item rs){ Item res; for(int i = 0; i < 3; i ++) res.once[i] = ls.once[i] + rs.once[i]; for(int i = 0; i < 3; i ++) for(int j = 0; j < 3; j ++) res.twice[i][j] = ls.twice[i][j] + rs.twice[i][j] + (ll)ls.once[i] * rs.once[j]; return res; } void pushup(int o){ tree[o] = merge(tree[ls(o)], tree[rs(o)]); } void addtag(int o, array<int, 3> mov){ Item lst = tree[o]; for(int i = 0; i < 3; i ++) tree[o].once[i] = 0; for(int i = 0; i < 3; i ++) for(int j = 0; j < 3; j ++) tree[o].twice[i][j] = 0; for(int i = 0; i < 3; i ++) tree[o].once[mov[i]] += lst.once[i]; for(int i = 0; i < 3; i ++) for(int j = 0; j < 3; j ++) tree[o].twice[mov[i]][mov[j]] += lst.twice[i][j]; array<int, 3> lzy = lazy[o]; for(int i = 0; i < 3; i ++) lazy[o][i] = mov[lzy[i]]; } void pushdown(int o){ addtag(ls(o), lazy[o]), addtag(rs(o), lazy[o]); lazy[o] = {0, 1, 2}; } public: Sgt(int _n = 0, vector<int> val = {}){ n = _n, tree = vector<Item>(4 * n + 100), lazy = vector<array<int, 3>>(4 * n + 100, {0, 1, 2}); auto build = [&](auto build, int o, int L, int R) -> void { if(L == R) return tree[o].once[val[L]] ++, void(); int M = (L + R) / 2; build(build, ls(o), L, M), build(build, rs(o), M + 1, R); pushup(o); } ; build(build, 1, 0, n - 1); } void modify(int l, int r, array<int, 3> mov){ auto modify = [&](auto modify, int o, int L, int R) -> void { if(l <= L && R <= r) return addtag(o, mov), void(); pushdown(o); int M = (L + R) / 2; if(l <= M) modify(modify, ls(o), L, M); if(r > M) modify(modify, rs(o), M + 1, R); pushup(o); } ; modify(modify, 1, 0, n - 1); } Item query(int l, int r){ auto query = [&](auto query, int o, int L, int R) -> Item { if(l <= L && R <= r) return tree[o]; pushdown(o); int M = (L + R) / 2; if(r <= M) return query(query, ls(o), L, M); if(l > M) return query(query, rs(o), M + 1, R); return merge(query(query, ls(o), L, M), query(query, rs(o), M + 1, R)); } ; return query(query, 1, 0, n - 1); } } ; void solve(){ int n, q; cin >> n >> q; vector<int> a(n + 1); for(int i = 1; i <= n; i ++) cin >> a[i]; Sgt sgt(n + 1, a); while(q --){ int opt; cin >> opt; if(opt == 1){ int L, R; cin >> L >> R; auto res = sgt.query(L, R); cout << res.twice[1][0] + res.twice[2][0] + res.twice[2][1] << "\n"; } if(opt == 2){ int L, R, S, T, U; cin >> L >> R >> S >> T >> U; sgt.modify(L, R, {S, T, U}); } } }
先考虑先手获胜的充要条件.
设第 \(i\) 行中先手的棋子位于格子 \(p _ i\), 后手的格子位于格子 \(q _ i\). 则 \(p _ i \neq q _ i\).
设\(P = \sum _ {i = 1} ^ {h} (p _i - 1), Q = \sum _ {i = 1} ^ {h} (w - q _ i)\).
若 \(P > Q\). 则先手第一步任意操作关键行. 对于后手的每一步:
于是此时先手必胜. (简而言之就是无论关键行谁赢, 最后先手都能耗死后手)
若 \(P < Q\). 同理可知此时后手必胜.
若 \(P = Q\). 则对于双方来说, 非关键行都是无用的. (因为若一方操作非关键行, 另一方必然操作关键行, 根据上面的讨论从而获胜. 故双方都只会在关键行操作完之后去操作非关键行, 此时的输赢和关键行内的输赢是相同的)
注意到关键行本质上是一个若干堆石头的 Nim
游戏, 所以此时先手获胜当且仅当 \(\bigoplus _ {i, p _ i > q _ i} (p _ i - q _ i - 1) \neq 0\).
所以设 \(S = \bigoplus _ {i, p _ i > q _ i} (p _ i - q _ i - 1)\), 则只需求 \(P > Q\) 或 \(P = Q\) 且 \(S > 0\) 的方案数.
设 \(f(i, T, S)\) 表示前 \(i\) 行, \(\sum _ {j = 1} ^ {i} (p _ j - 1) - \sum _ {j - 1} ^ {i} (w - q _ j) = T\), \(\bigoplus _ {1 \leq j \leq i, p _ j > q _ j} (p _ j - q _ j - 1) = S\) 的方案数.
则转移为 \(\begin{cases} \forall 1 \leq p < q \leq w, f(i, T, S) \xrightarrow{\sum} f(i + 1, T + (p - 1) - (w - q), S) \\ \forall 1 \leq q < p \leq w, f(i, T, S) \xrightarrow{\sum} f(i + 1, T, S \oplus (p - q - 1)) \end{cases}\).
可以优化为 \(\begin{cases} \forall - \! w \leq t \leq w, \left \lfloor \dfrac{w - |t|}{2} \right \rfloor f(i, T, S) \xrightarrow{\sum} f(i + 1, T + t, S) \\ \forall 0 \leq s \leq w - 1, (w - s - 1) f(i, T, S) \xrightarrow{\sum} f(i + 1, T, S \oplus s) \end{cases}\).
边界为 \(f(0, 0, 0) = 1\); 答案为 \(\sum _ {T > 0 \text{ or } T = 0 \text{ and } S > 0} f(h, T, S)\).
直接计算, 复杂度为 \(\mathcal{O}(h \cdot hw \cdot w \cdot w) = \mathcal{O}(h ^ 2 w ^ 3)\). 考虑优化.
事实上, \(T\) 的转移和 \(S\) 的转移互相独立. 考虑分开dp再合并起来.
于是设 \(f _ T (i, T)\) 表示有 \(i\) 行关键行, \(\sum _ {j = 1} ^ {i} (p _ j - 1) - \sum _ {j - 1} ^ {i} (w - q _ j) = T\) 的方案数.
设 \(f _ S (i, S)\) 表示有 \(i\) 行非关键行, \(\bigoplus _ {1 \leq j \leq i, p _ j > q _ j} (p _ j - q _ j - 1) = S\) 的方案数.
则转移为 \(\begin{cases} \forall - \! w \leq t \leq w, \left \lfloor \dfrac{w - |t|}{2} \right \rfloor f _ T (i, T) \xrightarrow{\sum} f _ T (i + 1, T + t) \\ \forall 0 \leq s \leq w - 1, (w - s - 1) f _ S (i, T, S) \xrightarrow{\sum} f _ S (i + 1, S \oplus s) \end{cases}\).
边界为 \(f _ T (0, 0) = f _ S (0, 0) = 1\); 答案为 \(\sum _ {k = 0} ^ {h} \sum _ {T > 0 \text{ or } T = 0 \text{ and } S > 0} f _ T (k, T) f _ S (h - k, S)\).
直接计算, 计算 \(f _ T\) 的复杂度为 \(\mathcal{O}(h \cdot hw \cdot w) = \mathcal{O}(h ^ 2 w ^ 2)\); 计算 \(f _ S\) 的复杂度为 \(\mathcal{O}(h \cdot w \cdot w) = \mathcal{O}(h w ^ 2)\); 计算答案的复杂度为 \(\mathcal{O}(h \cdot hw \cdot w) = \mathcal{O}(h ^ 2 w ^ 2)\).
先考虑优化答案的计算.
注意到答案为 \(\sum _ {k = 0} ^ {h} \sum _ {T > 0} f _ T (k, T) \left ( \dfrac{w (w - 1)}{2} \right ) ^ {h - k} + \sum _ {k = 0} ^ {h} f _ T (k, 0) \sum _ {S > 0} f _ S (h - k, S)\) 即可 \(\mathcal{O}(h \cdot hw + h \cdot w) = \mathcal{O}(h ^ 2 w)\) 计算.
再考虑优化 \(f _ T\) 的复杂度.
此时 \(f _ T (i, T) = \sum _ {-w \leq t \leq w} \left \lfloor \dfrac{w - |t|}{2} \right \rfloor f _ T(i - 1, T - t)\).
简便期间设 \(g _ T (T) = f _ T (i - 1, T)\). 考虑计算 \(\Delta = f _ T (i, T) - f _ T (i, T - 1)\).
则 \(\begin{array}{l} \Delta & = f _ T (i, T) - f _ T (i, T - 1) = \sum _ {-w \leq t \leq w} \left \lfloor \dfrac{w - |t|}{2} \right \rfloor (g _ T (T - t) - \ g _ T(T - 1 - t)) \\ & = \sum _ {-w \leq t \leq w} \left \lfloor \dfrac{w - |t|}{2} \right \rfloor g _ T (T - t) - \sum _ {-w + 1 \leq t \leq w + 1} \left \lfloor \dfrac{w - |t - 1|}{2} \right \rfloor g _ T (T - t) \\ & = \sum _ {-w + 1 \leq t \leq w} \left \lfloor \dfrac{w - |t|}{2} \right \rfloor g _ T (T - t) - \sum _ {-w + 1 \leq t \leq w} \left \lfloor \dfrac{w - |t - 1|}{2} \right \rfloor g _ T (T - t) \\ & = - \sum _ {1 \leq t \leq w} [2 \nmid w - t] g _ T (T - t) + \sum _ {-w + 1 \leq t \leq 0} [2 \mid w + t] g _ T (T - t) \end{array}\).
所以当 \(w\) 是奇数时, \(\Delta = g _ T (T + w - 2) + \cdots + g _ T (T + 1) - g _ T(T - 2) - \cdots - g _ T (T - w + 1)\).
当 \(w\) 是偶数时, \(\Delta = g _ T (T + w - 2) + \cdots + g _ T (T) - g _ T (T - 1) \cdots - g _ T (T - w + 1)\).
所以对 \(g _ T (*)\) 做奇偶分组的前缀和即可 \(\mathcal{O}(1)\) 计算 \(\Delta\).
而 \(f _ T (i, 0)\) 显然可以 \(\mathcal{O}(w)\) 计算. 所以复杂度变为 \(\mathcal{O}(h \cdot (w + hw)) = \mathcal{O}(h ^ 2 w)\).
所以就做完了. 总时间复杂度 \(\mathcal{O}(h ^ 2 w + h w ^ 2)\). 注意常数.
void solve(){ int h, w; cin >> h >> w; int maxS = 1 << (__lg(w) + 1); vector<vector<modint>> dpS(h + 1, vector<modint>(maxS + 1)); dpS[0][0] = modint(1); for(int i = 0; i < h; i ++) for(int S = 0; S <= maxS; S ++) if(dpS[i][S].val()) for(int s = 0; s < w; s ++) dpS[i + 1][S ^ s] += dpS[i][S] * modint(w - 1 - s); vector<modint> sumdpS(h + 1); for(int i = 0; i <= h; i ++) for(int S = 0; S <= maxS; S ++) sumdpS[i] += dpS[i][S]; Binom binom(h); modint ans(0); vector<modint> dpT((h + 1) * w + 1); dpT[0] = modint(1); for(int i = 0; i <= h; i ++){ for(int T = 1; T <= i * w; T ++) ans += binom.binom(h, i) * dpT[T] * sumdpS[h - i]; for(int S = 1; S <= maxS; S ++) ans += binom.binom(h, i) * dpT[0] * dpS[h - i][S]; if(i == h) break; vector<modint> sum((i + 1) * w + w + w + 1); for(int T = -w; T <= (i + 1) * w + w; T ++){ sum[T + w] = dpT[abs(T)]; if(T >= -w + 2) sum[T + w] += sum[T - 2 + w]; } vector<modint> newdpT((h + 1) * w + 1); for(int t = -w; t <= w; t ++) newdpT[0] += dpT[abs(t)] * modint((w - abs(t)) / 2); for(int T = 1; T <= (i + 1) * w; T ++){ if(w % 2 == 0){ newdpT[T] = newdpT[T - 1]; newdpT[T] += (sum[T + w - 2 + w] - sum[T - 2 + w]); newdpT[T] -= (sum[T - 1 + w] - sum[T - w - 1 + w]); } if(w % 2 == 1){ newdpT[T] = newdpT[T - 1]; newdpT[T] += (sum[T + w - 2 + w] - sum[T - 1 + w]); newdpT[T] -= (sum[T - 2 + w] - sum[T - w - 1 + w]); } } dpT = newdpT; } cout << ans.val() << "\n"; }