#include <bits/stdc++.h>
using namespace std;
// =====================================================================
// DP CLASSIC
// All Functions and Tricks
// Used in ECPC and ACPC competitions and advanced cases
// =====================================================================
// Global constant used in many DP functions (modulo for combinatorics, etc.)
// You must define MOD before using any function that relies on it.
const long long MOD = 1000000007LL;
// --------------------------------------------
// 1) Fibonacci (with memoization / bottom-up)
// --------------------------------------------
/*
* This section provides functions to compute Fibonacci numbers.
* - fib(n): top-down memoized recursion (requires resetting fibMemo to -1).
* - buildFib(n): bottom-up iterative, returns vector of fib[0..n].
* - fibOptimized(n): space-optimized O(1) iterative.
* - fibMatrix(n): matrix exponentiation O(log n) for large n.
* Use fibOptimized for small n, fibMatrix for n > 1e7.
* Note: global fibMemo must be reset using resetMemo().
*/
const int MAXN = 1e6 + 5;
long long fibMemo[MAXN];
/*
* Function: fib (recursive memoized)
* Description: Computes the n-th Fibonacci number using top-down DP with memoization.
* Parameters:
* n - non-negative integer (index of Fibonacci number)
* Returns: long long - the n-th Fibonacci number (F(0)=0, F(1)=1)
* Time Complexity: O(n)
* Space Complexity: O(n) for the memo array
* Constraints: n < MAXN (1e6+5)
* Notes: The global array fibMemo must be initialized to -1 before calling.
* Use resetMemo(fibMemo, MAXN) to reset.
*/
long long fib(int n) {
if (n <= 1) return n;
if (fibMemo[n] != -1) return fibMemo[n];
return fibMemo[n] = fib(n - 1) + fib(n - 2);
}
/*
* Function: buildFib (iterative)
* Description: Builds the Fibonacci sequence up to the n-th term using bottom-up DP.
* Parameters:
* n - non-negative integer (maximum index)
* Returns: vector<long long> - fib[0..n] where fib[i] = i-th Fibonacci number
* Time Complexity: O(n)
* Space Complexity: O(n)
* Constraints: n >= 0
* Notes: None
*/
vector<long long> buildFib(int n) {
vector<long long> fib(n + 1);
fib[0] = 0;
if (n >= 1) fib[1] = 1;
for (int i = 2; i <= n; i++) fib[i] = fib[i - 1] + fib[i - 2];
return fib;
}
// --------------------------------------------
// 2) Knapsack (0/1)
// --------------------------------------------
/*
* This section solves the classic 0/1 knapsack problem.
* - knapsack01: standard DP with O(n*W) time and O(W) space.
* - knapsack01ByValue: alternative when capacity W is huge but total value is small.
* Both assume each item is used at most once.
*/
/*
* Function: knapsack01
* Description: Solves the 0/1 knapsack problem: maximize total value with a weight capacity.
* Parameters:
* weight - vector<int> of item weights (size n)
* value - vector<int> of item values (size n)
* W - int capacity of knapsack
* Returns: long long - maximum total value achievable
* Time Complexity: O(n * W)
* Space Complexity: O(W) (using 1D DP array)
* Constraints: n and W can be up to ~10^4; weight[i] >= 0, W >= 0
* Notes: Items are distinct (each used at most once).
*/
long long knapsack01(const vector<int>& weight, const vector<int>& value, int W) {
int n = weight.size();
vector<long long> dp(W + 1, 0);
for (int i = 0; i < n; i++) {
for (int w = W; w >= weight[i]; w--) {
dp[w] = max(dp[w], dp[w - weight[i]] + value[i]);
}
}
return dp[W];
}
/*
* Function: knapsack01ByValue
* Description: Solves 0/1 knapsack when capacity W is very large but total value is small.
* It minimizes weight for each possible total value.
* Parameters:
* weight - vector<int> of item weights (size n)
* value - vector<int> of item values (size n)
* W - int capacity of knapsack
* Returns: long long - maximum total value that fits in capacity W
* Time Complexity: O(n * maxVal), where maxVal = sum of all values
* Space Complexity: O(maxVal)
* Constraints: sum of values should be manageable (e.g., <= 10^5) to avoid memory blow.
* Notes: Use this when W is huge and values are small.
*/
long long knapsack01ByValue(const vector<int>& weight, const vector<int>& value, int W) {
int n = weight.size();
int maxVal = accumulate(value.begin(), value.end(), 0);
const long long INF = 1e18;
vector<long long> dp(maxVal + 1, INF);
dp[0] = 0;
for (int i = 0; i < n; i++) {
for (int v = maxVal; v >= value[i]; v--) {
dp[v] = min(dp[v], dp[v - value[i]] + weight[i]);
}
}
long long ans = 0;
for (int v = 0; v <= maxVal; v++) {
if (dp[v] <= W) ans = v;
}
return ans;
}
// --------------------------------------------
// 3) Unbounded Knapsack (Complete)
// --------------------------------------------
/*
* Solves the unbounded knapsack problem: each item can be taken any number of times.
* - knapsackUnbounded: O(n*W) time, O(W) space.
*/
long long knapsackUnbounded(const vector<int>& weight, const vector<int>& value, int W) {
int n = weight.size();
vector<long long> dp(W + 1, 0);
for (int w = 0; w <= W; w++) {
for (int i = 0; i < n; i++) {
if (w >= weight[i]) {
dp[w] = max(dp[w], dp[w - weight[i]] + value[i]);
}
}
}
return dp[W];
}
// --------------------------------------------
// 4) LIS - Longest Increasing Subsequence
// O(N log N) using binary search
// --------------------------------------------
/*
* Functions for longest increasing subsequence and its variants.
* - LIS: strict increasing, O(n log n).
* - LNDS: non-decreasing (allows equal values), O(n log n).
* - LISReconstruct: reconstructs one LIS using O(n^2) DP (for n ≤ 5000).
* Use LIS for length only; use LISReconstruct for the actual sequence when n is small.
*/
int LIS(const vector<int>& arr) {
vector<int> lis;
for (int x : arr) {
auto it = lower_bound(lis.begin(), lis.end(), x);
if (it == lis.end()) lis.push_back(x);
else *it = x;
}
return lis.size();
}
int LNDS(const vector<int>& arr) {
vector<int> lis;
for (int x : arr) {
auto it = upper_bound(lis.begin(), lis.end(), x);
if (it == lis.end()) lis.push_back(x);
else *it = x;
}
return lis.size();
}
vector<int> LISReconstruct(const vector<int>& arr) {
int n = arr.size();
vector<int> dp(n, 1), parent(n, -1);
int maxLen = 1, bestIdx = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < i; j++) {
if (arr[j] < arr[i] && dp[j] + 1 > dp[i]) {
dp[i] = dp[j] + 1;
parent[i] = j;
}
}
if (dp[i] > maxLen) {
maxLen = dp[i];
bestIdx = i;
}
}
vector<int> seq;
for (int i = bestIdx; i != -1; i = parent[i]) seq.push_back(arr[i]);
reverse(seq.begin(), seq.end());
return seq;
}
// --------------------------------------------
// 5) LCS - Longest Common Subsequence
// --------------------------------------------
/*
* Computes the longest common subsequence between two strings.
* - LCS: returns length.
* - LCSReconstruct: returns the actual LCS string.
* - LCS_Optimized: uses O(min(n,m)) space.
* Time complexity O(n*m) for all; space O(n*m) for reconstruction, O(min) for optimized.
* Use LCS_Optimized when memory is tight.
*/
int LCS(const string& a, const string& b) {
int n = a.size(), m = b.size();
vector<vector<int>> dp(n + 1, vector<int>(m + 1, 0));
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (a[i - 1] == b[j - 1]) dp[i][j] = dp[i - 1][j - 1] + 1;
else dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
}
return dp[n][m];
}
string LCSReconstruct(const string& a, const string& b) {
int n = a.size(), m = b.size();
vector<vector<int>> dp(n + 1, vector<int>(m + 1, 0));
for (int i = 1; i <= n; i++)
for (int j = 1; j <= m; j++) {
if (a[i - 1] == b[j - 1]) dp[i][j] = dp[i - 1][j - 1] + 1;
else dp[i][j] = max(dp[i - 1][j], dp[i][j - 1]);
}
string res;
int i = n, j = m;
while (i > 0 && j > 0) {
if (a[i - 1] == b[j - 1]) {
res.push_back(a[i - 1]);
i--, j--;
} else if (dp[i - 1][j] > dp[i][j - 1]) i--;
else j--;
}
reverse(res.begin(), res.end());
return res;
}
// --------------------------------------------
// 6) Coin Change (minimum coins & number of ways)
// --------------------------------------------
/*
* Solves coin change problems with unlimited supply.
* - coinChangeMin: minimum number of coins to make amount.
* - coinChangeWays: number of combinations to make amount (order does not matter).
* Both use O(amount * |coins|) time and O(amount) space.
*/
int coinChangeMin(const vector<int>& coins, int amount) {
const int INF = 1e9;
vector<int> dp(amount + 1, INF);
dp[0] = 0;
for (int i = 1; i <= amount; i++) {
for (int c : coins) {
if (i >= c) dp[i] = min(dp[i], dp[i - c] + 1);
}
}
return dp[amount] == INF ? -1 : dp[amount];
}
long long coinChangeWays(const vector<int>& coins, int amount) {
vector<long long> dp(amount + 1, 0);
dp[0] = 1;
for (int c : coins) {
for (int i = c; i <= amount; i++) {
dp[i] += dp[i - c];
}
}
return dp[amount];
}
// --------------------------------------------
// 7) Matrix Chain Multiplication
// --------------------------------------------
/*
* Computes minimum scalar multiplications to multiply a chain of matrices.
* - matrixChainOrder: classic interval DP, O(n^3) time, O(n^2) space.
* n = dims.size() - 1 (number of matrices).
*/
int matrixChainOrder(const vector<int>& dims) {
int n = dims.size() - 1;
vector<vector<int>> dp(n, vector<int>(n, 0));
for (int len = 2; len <= n; len++) {
for (int i = 0; i <= n - len; i++) {
int j = i + len - 1;
dp[i][j] = INT_MAX;
for (int k = i; k < j; k++) {
int cost = dp[i][k] + dp[k + 1][j] + dims[i] * dims[k + 1] * dims[j + 1];
dp[i][j] = min(dp[i][j], cost);
}
}
}
return dp[0][n - 1];
}
// --------------------------------------------
// 8) Edit Distance (Levenshtein)
// --------------------------------------------
/*
* Computes minimum operations (insert, delete, replace) to transform one string into another.
* - editDistance: standard DP, O(n*m) time and space.
* Can be optimized to O(min(n,m)) space, but not implemented here.
*/
int editDistance(const string& a, const string& b) {
int n = a.size(), m = b.size();
vector<vector<int>> dp(n + 1, vector<int>(m + 1, 0));
for (int i = 0; i <= n; i++) dp[i][0] = i;
for (int j = 0; j <= m; j++) dp[0][j] = j;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (a[i - 1] == b[j - 1]) dp[i][j] = dp[i - 1][j - 1];
else dp[i][j] = 1 + min({dp[i - 1][j], dp[i][j - 1], dp[i - 1][j - 1]});
}
}
return dp[n][m];
}
// --------------------------------------------
// 9) DP on Grid / Number of Paths
// --------------------------------------------
/*
* Counting paths in a grid (right/down moves) and variants.
* - countPaths: number of paths from (0,0) to (n-1,m-1).
* - countPathsWithObstacles: with blocked cells.
* - minPathSum: minimum sum path.
* All use O(n*m) time and space (can be optimized to O(m)).
*/
long long countPaths(int n, int m) {
vector<vector<long long>> dp(n, vector<long long>(m, 0));
dp[0][0] = 1;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (i > 0) dp[i][j] += dp[i - 1][j];
if (j > 0) dp[i][j] += dp[i][j - 1];
}
}
return dp[n - 1][m - 1];
}
long long countPathsWithObstacles(const vector<vector<int>>& grid) {
int n = grid.size(), m = grid[0].size();
if (grid[0][0] == 1 || grid[n - 1][m - 1] == 1) return 0;
vector<vector<long long>> dp(n, vector<long long>(m, 0));
dp[0][0] = 1;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (grid[i][j] == 1) continue;
if (i > 0) dp[i][j] += dp[i - 1][j];
if (j > 0) dp[i][j] += dp[i][j - 1];
}
}
return dp[n - 1][m - 1];
}
int minPathSum(const vector<vector<int>>& grid) {
int n = grid.size(), m = grid[0].size();
vector<vector<int>> dp(n, vector<int>(m, INT_MAX));
dp[0][0] = grid[0][0];
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (i > 0) dp[i][j] = min(dp[i][j], dp[i - 1][j] + grid[i][j]);
if (j > 0) dp[i][j] = min(dp[i][j], dp[i][j - 1] + grid[i][j]);
}
}
return dp[n - 1][m - 1];
}
// --------------------------------------------
// 10) Maximum Subarray Sum (Kadane) and variants
// --------------------------------------------
/*
* Kadane's algorithm and its extensions.
* - maxSubarraySumKadane: classic O(n) for max sum (allows negative).
* - maxSubarraySumAtMostK: max sum with length at most K, O(n log n) using multiset.
* - maxSubarraySumAtMostK2: same but O(n) using deque.
* - maxProductSubarray: maximum product subarray, O(n).
*/
long long maxSubarraySumKadane(const vector<long long>& arr) {
long long maxEnd = 0, maxSum = LLONG_MIN;
for (long long x : arr) {
maxEnd = max(x, maxEnd + x);
maxSum = max(maxSum, maxEnd);
}
return maxSum;
}
long long maxSubarraySumAtMostK(const vector<long long>& arr, int K) {
int n = arr.size();
vector<long long> pref(n + 1, 0);
for (int i = 0; i < n; i++) pref[i + 1] = pref[i] + arr[i];
multiset<long long> ms;
long long ans = LLONG_MIN;
for (int i = 0; i <= n; i++) {
if (i - K - 1 >= 0) ms.erase(ms.find(pref[i - K - 1]));
if (!ms.empty()) ans = max(ans, pref[i] - *ms.begin());
ms.insert(pref[i]);
}
return ans;
}
long long maxSubarraySumAtMostK2(const vector<long long>& arr, int K) {
int n = arr.size();
vector<long long> pref(n + 1, 0);
for (int i = 0; i < n; i++) pref[i + 1] = pref[i] + arr[i];
deque<int> dq;
dq.push_back(0);
long long ans = LLONG_MIN;
for (int i = 1; i <= n; i++) {
while (!dq.empty() && dq.front() < i - K) dq.pop_front();
ans = max(ans, pref[i] - pref[dq.front()]);
while (!dq.empty() && pref[dq.back()] >= pref[i]) dq.pop_back();
dq.push_back(i);
}
return ans;
}
long long maxProductSubarray(const vector<int>& nums) {
long long maxProd = nums[0], minProd = nums[0], ans = nums[0];
for (int i = 1; i < (int)nums.size(); i++) {
long long a = maxProd * nums[i];
long long b = minProd * nums[i];
maxProd = max({1LL * nums[i], a, b});
minProd = min({1LL * nums[i], a, b});
ans = max(ans, maxProd);
}
return ans;
}
// --------------------------------------------
// 11) DP with Bitmask (TSP, Hamiltonian Path)
// --------------------------------------------
/*
* Bitmask DP for problems with small n (n ≤ 20).
* - tsp: Traveling Salesman Problem (min cost Hamiltonian cycle).
* - countHamiltonianPaths: count number of Hamiltonian paths (directed graph).
* - countHamiltonianPaths(src, dst): with fixed start/end.
* Time O(2^n * n^2) or O(2^n * n).
*/
int tsp(int n, const vector<vector<int>>& dist) {
vector<vector<int>> dp(1 << n, vector<int>(n, INT_MAX));
dp[1][0] = 0; // start from city 0
for (int mask = 1; mask < (1 << n); mask++) {
for (int u = 0; u < n; u++) {
if (!(mask & (1 << u))) continue;
if (dp[mask][u] == INT_MAX) continue;
for (int v = 0; v < n; v++) {
if (mask & (1 << v)) continue;
int newMask = mask | (1 << v);
dp[newMask][v] = min(dp[newMask][v], dp[mask][u] + dist[u][v]);
}
}
}
int fullMask = (1 << n) - 1;
int ans = INT_MAX;
for (int u = 0; u < n; u++) {
if (dp[fullMask][u] != INT_MAX)
ans = min(ans, dp[fullMask][u] + dist[u][0]);
}
return ans;
}
long long countHamiltonianPaths(int n, const vector<vector<int>>& adj) {
vector<vector<long long>> dp(1 << n, vector<long long>(n, 0));
for (int i = 0; i < n; i++) dp[1 << i][i] = 1;
for (int mask = 1; mask < (1 << n); mask++) {
for (int u = 0; u < n; u++) {
if (!(mask & (1 << u))) continue;
if (dp[mask][u] == 0) continue;
for (int v = 0; v < n; v++) {
if (mask & (1 << v)) continue;
if (!adj[u][v]) continue;
dp[mask | (1 << v)][v] += dp[mask][u];
}
}
}
long long ans = 0;
for (int i = 0; i < n; i++) ans += dp[(1 << n) - 1][i];
return ans;
}
long long countHamiltonianPaths(int n, int src, int dst, const vector<vector<int>>& adj) {
vector<vector<long long>> dp(1 << n, vector<long long>(n, 0));
dp[1 << src][src] = 1;
for (int mask = 1; mask < (1 << n); mask++) {
for (int u = 0; u < n; u++) {
if (!(mask & (1 << u)) || dp[mask][u] == 0) continue;
for (int v : adj[u]) {
if (mask & (1 << v)) continue;
dp[mask | (1 << v)][v] += dp[mask][u];
}
}
}
return dp[(1 << n) - 1][dst];
}
// --------------------------------------------
// 12) DP on Trees (Tree DP)
// --------------------------------------------
/*
* Tree DP functions.
* - dfsTreeDP: computes maximum independent set on a tree (global tree, dp0, dp1).
* - dfsDiameter: computes tree diameter (longest path).
* - dfsDown/dfsUp: rerooting DP to compute subtree contributions from all directions.
* Use global adjacency list 'tree'.
*/
vector<vector<int>> tree;
vector<int> dp0, dp1; // dp0 = not taken, dp1 = taken
void dfsTreeDP(int u, int p) {
dp1[u] = 1;
for (int v : tree[u]) {
if (v == p) continue;
dfsTreeDP(v, u);
dp0[u] += max(dp0[v], dp1[v]);
dp1[u] += dp0[v];
}
}
pair<int, int> dfsDiameter(int u, int p) {
int max1 = 0, max2 = 0;
for (int v : tree[u]) {
if (v == p) continue;
auto [d1, d2] = dfsDiameter(v, u);
max1 = max(max1, d1 + 1);
max2 = max(max2, d2);
}
return {max1, max(max2, max1 + max2)};
}
vector<long long> dpDown, dpUp, ansTree; // for rerooting
void dfsDown(int u, int p) {
dpDown[u] = 1; // example: count nodes in subtree
for (int v : tree[u]) {
if (v == p) continue;
dfsDown(v, u);
dpDown[u] += dpDown[v];
}
}
void dfsUp(int u, int p) {
int deg = tree[u].size();
vector<long long> pref(deg + 1, 0), suff(deg + 1, 0);
for (int i = 0; i < deg; i++) {
int v = tree[u][i];
pref[i + 1] = pref[i] + (v == p ? dpUp[u] : dpDown[v]);
}
for (int i = deg - 1; i >= 0; i--) {
int v = tree[u][i];
suff[i] = suff[i + 1] + (v == p ? dpUp[u] : dpDown[v]);
}
for (int i = 0; i < deg; i++) {
int v = tree[u][i];
if (v == p) continue;
dpUp[v] = 1 + pref[i] + suff[i + 1]; // combine all other branches
dfsUp(v, u);
}
}
// Usage: dfsDown(0, -1); dpUp[0] = 0; dfsUp(0, -1);
// answer for each node = dpDown[u] + dpUp[u] (or other definitions)
// --------------------------------------------
// 13) Digit DP
// --------------------------------------------
/*
* Digit DP for counting numbers with certain digit sum or other properties.
* - countDigitSum: counts numbers in [0, N] whose digit sum equals S.
* - countDigitSumWithLeadingZero: counts numbers with exactly target occurrences of digit 1 (or any digit).
* Uses memoization. Complexity O(len * sum * 2).
* Note: N ≤ 1e18 (len ≤ 19).
*/
long long dpDigit[20][200][2];
string numStr;
long long solveDigitDP(int pos, int sum, int tight, int S) {
if (pos == (int)numStr.size()) return sum == S;
long long &ret = dpDigit[pos][sum][tight];
if (ret != -1) return ret;
ret = 0;
int limit = tight ? numStr[pos] - '0' : 9;
for (int d = 0; d <= limit; d++) {
ret += solveDigitDP(pos + 1, sum + d, tight && (d == limit), S);
}
return ret;
}
long long countDigitSum(long long N, int S) {
numStr = to_string(N);
memset(dpDigit, -1, sizeof(dpDigit));
return solveDigitDP(0, 0, 1, S);
}
// Variant with leading zeros and counting a specific digit (e.g., digit 1)
long long memoLead[20][20][2][2]; // [pos][cnt][tight][started]
long long solveDigitDPWithLeadingZero(int pos, int cnt, bool tight, bool started, int target, const string& num) {
if (pos == (int)num.size()) return (started && cnt == target) ? 1 : 0;
long long &ret = memoLead[pos][cnt][tight][started];
if (ret != -1) return ret;
ret = 0;
int limit = tight ? num[pos] - '0' : 9;
for (int d = 0; d <= limit; d++) {
bool ntight = tight && (d == limit);
bool nstarted = started || (d != 0);
int add = (d == 1 && nstarted) ? 1 : 0;
ret += solveDigitDPWithLeadingZero(pos + 1, cnt + add, ntight, nstarted, target, num);
}
return ret;
}
long long countDigitSumWithLeadingZero(long long N, int target) {
string s = to_string(N);
memset(memoLead, -1, sizeof(memoLead));
return solveDigitDPWithLeadingZero(0, 0, true, false, target, s);
}
// --------------------------------------------
// 14) DP with Prefix Sum Optimization
// --------------------------------------------
/*
* Optimizes DP transitions by using prefix sums to reduce time from O(K*n*limit) to O(K*n).
* - dpPrefixOpt: counts ways to reach sum n using exactly K moves, each move adds 1..limit.
* Complexity O(K*n), space O(n).
*/
long long dpPrefixOpt(int n, int K, int limit) {
vector<long long> dp(n + 1, 0), pref(n + 1, 0);
dp[0] = 1;
for (int step = 0; step < K; step++) {
for (int i = 1; i <= n; i++) pref[i] = (pref[i - 1] + dp[i]) % MOD;
vector<long long> ndp(n + 1, 0);
for (int i = 1; i <= n; i++) {
int l = max(0, i - limit);
ndp[i] = (pref[i - 1] - (l > 0 ? pref[l - 1] : 0) + MOD) % MOD;
}
dp = ndp;
}
long long ans = 0;
for (int i = 0; i <= n; i++) ans = (ans + dp[i]) % MOD;
return ans;
}
// --------------------------------------------
// 15) DP + Sliding Window / Monotonic Queue
// --------------------------------------------
/*
* Monotonic queue optimization for DP with sliding window maximum.
* - maxSumWithWindow: example (may need verification; provided as template).
* Usually used for problems like "max sum of subsequence with gap constraint".
* Complexity O(n).
*/
long long maxSumWithWindow(const vector<long long>& arr, int K) {
int n = arr.size();
vector<long long> dp(n + 1, 0);
deque<int> dq;
for (int i = 1; i <= n; i++) {
if (!dq.empty() && dq.front() < i - K) dq.pop_front();
dp[i] = dp[i - 1] + arr[i - 1];
if (!dq.empty()) dp[i] = max(dp[i], dp[dq.front()] + arr[i - 1]);
while (!dq.empty() && dp[dq.back()] <= dp[i]) dq.pop_back();
dq.push_back(i);
}
return dp[n];
}
// --------------------------------------------
// 16) DP + Bitmask Subsets (SOS DP)
// --------------------------------------------
/*
* Sum Over Subsets DP (SOS DP) to compute f[mask] = sum_{sub ⊆ mask} a[sub].
* - sosDP: in-place transform, O(n * 2^n) time, O(2^n) space.
* Used for subset convolution and related problems.
*/
vector<int> sosDP(const vector<int>& a) {
int n = a.size();
int N = 1 << n;
vector<int> f = a;
for (int i = 0; i < n; i++) {
for (int mask = 0; mask < N; mask++) {
if (mask & (1 << i)) f[mask] += f[mask ^ (1 << i)];
}
}
return f;
}
// --------------------------------------------
// 17) DP with Monotonic Stack (for histograms etc.)
// --------------------------------------------
/*
* Largest rectangle in histogram using monotonic stack.
* - largestRectangleArea: O(n) time, O(n) space.
*/
long long largestRectangleArea(const vector<int>& heights) {
int n = heights.size();
vector<int> left(n), right(n);
stack<int> st;
for (int i = 0; i < n; i++) {
while (!st.empty() && heights[st.top()] >= heights[i]) st.pop();
left[i] = st.empty() ? -1 : st.top();
st.push(i);
}
while (!st.empty()) st.pop();
for (int i = n - 1; i >= 0; i--) {
while (!st.empty() && heights[st.top()] >= heights[i]) st.pop();
right[i] = st.empty() ? n : st.top();
st.push(i);
}
long long ans = 0;
for (int i = 0; i < n; i++) {
ans = max(ans, 1LL * heights[i] * (right[i] - left[i] - 1));
}
return ans;
}
// --------------------------------------------
// 18) DP + Combinatorics (NCR, precomputation)
// --------------------------------------------
/*
* Precomputation of factorials and inverse factorials for nCr modulo MOD.
* - initComb(): must be called once before using nCr.
* - nCr(n,r): returns C(n,r) mod MOD in O(1).
* MOD must be prime.
*/
const int MAXC = 1000005;
long long fact[MAXC], invFact[MAXC];
long long modPow(long long a, long long e) {
long long r = 1;
while (e) {
if (e & 1) r = (r * a) % MOD;
a = (a * a) % MOD;
e >>= 1;
}
return r;
}
void initComb() {
fact[0] = 1;
for (int i = 1; i < MAXC; i++) fact[i] = fact[i - 1] * i % MOD;
invFact[MAXC - 1] = modPow(fact[MAXC - 1], MOD - 2);
for (int i = MAXC - 2; i >= 0; i--) invFact[i] = invFact[i + 1] * (i + 1) % MOD;
}
long long nCr(int n, int r) {
if (r < 0 || r > n) return 0;
return fact[n] * invFact[r] % MOD * invFact[n - r] % MOD;
}
// --------------------------------------------
// 19) DP for Longest Palindromic Subsequence
// --------------------------------------------
/*
* Longest Palindromic Subsequence (LPS) length.
* - LPS: interval DP, O(n^2) time and space.
*/
int LPS(const string& s) {
int n = s.size();
vector<vector<int>> dp(n, vector<int>(n, 0));
for (int i = 0; i < n; i++) dp[i][i] = 1;
for (int len = 2; len <= n; len++) {
for (int i = 0; i <= n - len; i++) {
int j = i + len - 1;
if (s[i] == s[j]) dp[i][j] = dp[i + 1][j - 1] + 2;
else dp[i][j] = max(dp[i + 1][j], dp[i][j - 1]);
}
}
return dp[0][n - 1];
}
// --------------------------------------------
// 20) DP for Longest Common Substring (LCSubstr)
// --------------------------------------------
/*
* Longest common substring (contiguous) between two strings.
* - longestCommonSubstring: O(n*m) time and space.
*/
int longestCommonSubstring(const string& a, const string& b) {
int n = a.size(), m = b.size();
vector<vector<int>> dp(n + 1, vector<int>(m + 1, 0));
int ans = 0;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (a[i - 1] == b[j - 1]) {
dp[i][j] = dp[i - 1][j - 1] + 1;
ans = max(ans, dp[i][j]);
}
}
}
return ans;
}
// --------------------------------------------
// 21) DP with Rolling Array (Space Optimization)
// --------------------------------------------
/*
* Space-optimized iterative Fibonacci.
* - fibOptimized: O(1) space, O(n) time.
*/
long long fibOptimized(int n) {
if (n <= 1) return n;
long long a = 0, b = 1;
for (int i = 2; i <= n; i++) {
long long c = a + b;
a = b;
b = c;
}
return b;
}
// --------------------------------------------
// 22) DP for Partition Problems
// --------------------------------------------
/*
* Partition into two subsets with equal sum.
* - canPartition: O(n * target) time, O(target) space.
*/
bool canPartition(const vector<int>& nums) {
int sum = accumulate(nums.begin(), nums.end(), 0);
if (sum & 1) return false;
int target = sum / 2;
vector<bool> dp(target + 1, false);
dp[0] = true;
for (int x : nums) {
for (int s = target; s >= x; s--) {
if (dp[s - x]) dp[s] = true;
}
}
return dp[target];
}
// --------------------------------------------
// 23) DP + Divide and Conquer Optimization
// --------------------------------------------
/*
* Divide and Conquer DP optimization for DP of the form:
* dp[i][j] = min_{k < j} (dp[i-1][k] + cost(k, j))
* when the optimal split point is monotonic.
* - compute: template function; adapt cost function accordingly.
* Complexity O(m * n log n) for each row.
*/
void computeDC(int l, int r, int optL, int optR, int k, vector<vector<long long>>& dp, const vector<long long>& pref) {
if (l > r) return;
int mid = (l + r) / 2;
int bestOpt = optL;
long long bestVal = LLONG_MAX;
for (int i = optL; i <= min(optR, mid - 1); i++) {
long long cost = dp[k - 1][i] + (pref[mid] - pref[i]) * (pref[mid] - pref[i]); // example cost
if (cost < bestVal) {
bestVal = cost;
bestOpt = i;
}
}
dp[k][mid] = bestVal;
computeDC(l, mid - 1, optL, bestOpt, k, dp, pref);
computeDC(mid + 1, r, bestOpt, optR, k, dp, pref);
}
// --------------------------------------------
// 24) DP + Knuth Optimization (mentioned)
// --------------------------------------------
// Knuth optimization can be applied when quadrangle inequality holds.
// Not implemented as a separate function.
// --------------------------------------------
// 25) DP + Bitmask (Counting subsets with sum)
// --------------------------------------------
/*
* Count subsets with a given sum (0/1 knapsack counting).
* - countSubsetsWithSum: O(n * target), O(target).
*/
long long countSubsetsWithSum(const vector<int>& arr, int target) {
int n = arr.size();
vector<long long> dp(target + 1, 0);
dp[0] = 1;
for (int x : arr) {
for (int s = target; s >= x; s--) {
dp[s] += dp[s - x];
}
}
return dp[target];
}
// --------------------------------------------
// 26) DP + Meet-in-the-Middle (for large N up to 40)
// --------------------------------------------
/*
* Meet-in-the-Middle for subset sum problems when n ≤ 40.
* - getSubsetSums: generates all subset sums of a half-array.
* - countSubsetsWithSumMITM: counts subsets with exact target.
* Complexity O(2^(n/2) log 2^(n/2)).
*/
vector<long long> getSubsetSums(const vector<int>& arr) {
vector<long long> sums;
int n = arr.size();
for (int mask = 0; mask < (1 << n); mask++) {
long long sum = 0;
for (int i = 0; i < n; i++) {
if (mask & (1 << i)) sum += arr[i];
}
sums.push_back(sum);
}
return sums;
}
long long countSubsetsWithSumMITM(const vector<int>& arr, int target) {
int n = arr.size();
vector<int> left(arr.begin(), arr.begin() + n / 2);
vector<int> right(arr.begin() + n / 2, arr.end());
vector<long long> L = getSubsetSums(left), R = getSubsetSums(right);
sort(R.begin(), R.end());
long long ans = 0;
for (long long s : L) {
ans += upper_bound(R.begin(), R.end(), target - s) - lower_bound(R.begin(), R.end(), target - s);
}
return ans;
}
// --------------------------------------------
// 27) DP + Matrix Exponentiation (for linear recurrences)
// --------------------------------------------
/*
* Matrix exponentiation for linear recurrences.
* - Matrix struct for n x n matrices with multiplication modulo MOD.
* - matPow: exponentiation.
* - fibMatrix: Fibonacci using matrix exponentiation O(log n).
* - linearRecurrence: computes n-th term of a 2nd order recurrence.
*/
struct Matrix {
vector<vector<long long>> mat;
int n;
Matrix(int n, bool identity = false) : n(n), mat(n, vector<long long>(n, 0)) {
if (identity) for (int i = 0; i < n; i++) mat[i][i] = 1;
}
Matrix operator*(const Matrix& other) const {
Matrix res(n);
for (int i = 0; i < n; i++) {
for (int k = 0; k < n; k++) {
if (mat[i][k] == 0) continue;
for (int j = 0; j < n; j++) {
res.mat[i][j] = (res.mat[i][j] + mat[i][k] * other.mat[k][j]) % MOD;
}
}
}
return res;
}
};
Matrix matPow(Matrix base, long long exp) {
Matrix res(base.n, true);
while (exp > 0) {
if (exp & 1) res = res * base;
base = base * base;
exp >>= 1;
}
return res;
}
long long fibMatrix(int n) {
if (n <= 1) return n;
Matrix base(2);
base.mat = {{1, 1}, {1, 0}};
Matrix res = matPow(base, n - 1);
return res.mat[0][0];
}
long long linearRecurrence(long long n, long long a, long long b, long long f0, long long f1) {
if (n == 0) return f0;
if (n == 1) return f1;
Matrix base(2);
base.mat = {{a, b}, {1, 0}};
Matrix res = matPow(base, n - 1);
return (res.mat[0][0] * f1 + res.mat[0][1] * f0) % MOD;
}
// --------------------------------------------
// 28) DP for "Minimum Operations" (like min steps to reduce to 1)
// --------------------------------------------
/*
* Minimum steps to reduce n to 1 using operations: subtract 1, divide by 2 (if even), divide by 3 (if divisible by 3).
* - minStepsToOne: O(n) time and space.
*/
int minStepsToOne(int n) {
vector<int> dp(n + 1, 1e9);
dp[1] = 0;
for (int i = 2; i <= n; i++) {
dp[i] = dp[i - 1] + 1;
if (i % 2 == 0) dp[i] = min(dp[i], dp[i / 2] + 1);
if (i % 3 == 0) dp[i] = min(dp[i], dp[i / 3] + 1);
}
return dp[n];
}
// --------------------------------------------
// 29) DP for "Jump Game" / "Minimum Jumps to Reach End"
// --------------------------------------------
/*
* Minimum number of jumps to reach the end.
* - minJumps: O(n^2) DP (for small n).
* - minJumpsGreedy: O(n) greedy for reachable arrays.
*/
int minJumps(const vector<int>& nums) {
int n = nums.size();
vector<int> dp(n, INT_MAX);
dp[0] = 0;
for (int i = 0; i < n; i++) {
if (dp[i] == INT_MAX) continue;
for (int j = i + 1; j <= min(n - 1, i + nums[i]); j++) {
dp[j] = min(dp[j], dp[i] + 1);
}
}
return dp[n - 1];
}
int minJumpsGreedy(const vector<int>& nums) {
int n = nums.size();
if (n <= 1) return 0;
int jumps = 0, currEnd = 0, farthest = 0;
for (int i = 0; i < n - 1; i++) {
farthest = max(farthest, i + nums[i]);
if (i == currEnd) {
jumps++;
currEnd = farthest;
}
}
return jumps;
}
// --------------------------------------------
// 30) DP for "Word Break" (Can be segmented)
// --------------------------------------------
/*
* Determines if a string can be segmented into dictionary words.
* - wordBreak: O(n^2) time with hash set.
*/
bool wordBreak(const string& s, const vector<string>& wordDict) {
unordered_set<string> dict(wordDict.begin(), wordDict.end());
int n = s.size();
vector<bool> dp(n + 1, false);
dp[0] = true;
for (int i = 1; i <= n; i++) {
for (int j = 0; j < i; j++) {
if (dp[j] && dict.count(s.substr(j, i - j))) {
dp[i] = true;
break;
}
}
}
return dp[n];
}
// --------------------------------------------
// 31) DP for "Number of Ways to Decode" (Leetcode 91)
// --------------------------------------------
/*
* Counts ways to decode a digit string (A=1,...,Z=26).
* - numDecodings: O(n) time and space.
*/
int numDecodings(const string& s) {
int n = s.size();
vector<long long> dp(n + 1, 0);
dp[0] = 1;
for (int i = 1; i <= n; i++) {
if (s[i - 1] != '0') dp[i] += dp[i - 1];
if (i >= 2) {
int two = (s[i - 2] - '0') * 10 + (s[i - 1] - '0');
if (two >= 10 && two <= 26) dp[i] += dp[i - 2];
}
}
return dp[n];
}
// --------------------------------------------
// 32) DP + KMP Automaton (for avoiding patterns)
// --------------------------------------------
/*
* Count strings of length n over lowercase letters that do NOT contain a given pattern.
* - buildKMPAutomaton: builds transition table.
* - countStringsWithoutPattern: DP using automaton.
* Complexity O(n * m * 26).
*/
vector<array<int, 26>> buildKMPAutomaton(const string& pat) {
int m = pat.size();
vector<array<int, 26>> aut(m + 1);
vector<int> pi(m, 0);
for (int i = 1; i < m; i++) {
int j = pi[i - 1];
while (j > 0 && pat[i] != pat[j]) j = pi[j - 1];
if (pat[i] == pat[j]) j++;
pi[i] = j;
}
for (int i = 0; i <= m; i++) {
for (int c = 0; c < 26; c++) {
if (i < m && pat[i] == 'a' + c) aut[i][c] = i + 1;
else if (i == 0) aut[i][c] = 0;
else aut[i][c] = aut[pi[i - 1]][c];
}
}
return aut;
}
long long countStringsWithoutPattern(int n, const string& pat, long long MOD) {
auto aut = buildKMPAutomaton(pat);
int m = pat.size();
vector<vector<long long>> dp(n + 1, vector<long long>(m + 1, 0));
dp[0][0] = 1;
for (int i = 0; i < n; i++) {
for (int state = 0; state < m; state++) {
for (int c = 0; c < 26; c++) {
int nxt = aut[state][c];
if (nxt == m) continue;
dp[i + 1][nxt] = (dp[i + 1][nxt] + dp[i][state]) % MOD;
}
}
}
long long ans = 0;
for (int state = 0; state < m; state++) ans = (ans + dp[n][state]) % MOD;
return ans;
}
// --------------------------------------------
// 33) DP for "Distinct Subsequences"
// --------------------------------------------
/*
* Count number of distinct subsequences (including empty) of a string.
* - distinctSubsequences: O(n) time and space (using last occurrence).
* Returns total distinct subsequences minus 1 (excluding empty).
*/
int distinctSubsequences(const string& s) {
int n = s.size();
vector<long long> dp(n + 1, 0);
dp[0] = 1;
vector<int> last(26, -1);
for (int i = 1; i <= n; i++) {
dp[i] = (2 * dp[i - 1]) % MOD;
int c = s[i - 1] - 'a';
if (last[c] != -1) dp[i] = (dp[i] - dp[last[c] - 1] + MOD) % MOD;
last[c] = i;
}
return dp[n] - 1; // exclude empty
}
// --------------------------------------------
// 34) DP for "Minimum Path Cover in DAG" (not implemented)
// --------------------------------------------
// (See bipartite matching for DAG path cover.)
// --------------------------------------------
// 35) DP + Topological Sort (DAG DP)
// --------------------------------------------
/*
* Longest path in a weighted DAG.
* - topologicalSort: computes topological order.
* - dagLongestPath: returns maximum path sum (vertex weights).
* Complexity O(V + E).
*/
vector<int> topo;
void topologicalSort(int n, vector<vector<int>>& adj) {
vector<int> indeg(n, 0);
for (int u = 0; u < n; u++) for (int v : adj[u]) indeg[v]++;
queue<int> q;
for (int i = 0; i < n; i++) if (indeg[i] == 0) q.push(i);
while (!q.empty()) {
int u = q.front(); q.pop();
topo.push_back(u);
for (int v : adj[u]) {
if (--indeg[v] == 0) q.push(v);
}
}
}
long long dagLongestPath(int n, vector<vector<int>>& adj, vector<int>& weight) {
topologicalSort(n, adj);
vector<long long> dp(n, 0);
for (int u : topo) {
for (int v : adj[u]) {
dp[v] = max(dp[v], dp[u] + weight[v]);
}
}
return *max_element(dp.begin(), dp.end());
}
// --------------------------------------------
// 36) DP + Game Theory (optimal play)
// --------------------------------------------
/*
* Predict the winner in a game where players pick from ends.
* - predictTheWinner: returns true if first player can win.
* O(n^2) time and space.
*/
bool predictTheWinner(vector<int>& nums) {
int n = nums.size();
vector<vector<int>> dp(n, vector<int>(n, 0));
for (int i = 0; i < n; i++) dp[i][i] = nums[i];
for (int len = 2; len <= n; len++) {
for (int i = 0; i <= n - len; i++) {
int j = i + len - 1;
dp[i][j] = max(nums[i] - dp[i + 1][j], nums[j] - dp[i][j - 1]);
}
}
return dp[0][n - 1] >= 0;
}
// --------------------------------------------
// 37) DP + "Maximum Sum Rectangle in 2D" (Kadane on rows)
// --------------------------------------------
/*
* Maximum sum sub-rectangle in a 2D matrix.
* - maxSumRectangle: O(n^2 * m) time, O(m) space.
* Works for both int and long long matrices (overloaded).
*/
long long maxSumRectangle(const vector<vector<int>>& mat) {
int n = mat.size(), m = mat[0].size();
long long best = LLONG_MIN;
for (int top = 0; top < n; top++) {
vector<long long> col(m, 0);
for (int bottom = top; bottom < n; bottom++) {
for (int j = 0; j < m; j++) col[j] += mat[bottom][j];
long long cur = 0, mx = LLONG_MIN;
for (int j = 0; j < m; j++) {
cur = max(col[j], cur + col[j]);
mx = max(mx, cur);
}
best = max(best, mx);
}
}
return best;
}
// Overload for long long matrix
long long maxSumRectangle(const vector<vector<long long>>& matrix) {
int n = matrix.size(), m = matrix[0].size();
long long ans = LLONG_MIN;
for (int top = 0; top < n; top++) {
vector<long long> colSum(m, 0);
for (int bottom = top; bottom < n; bottom++) {
for (int j = 0; j < m; j++) colSum[j] += matrix[bottom][j];
long long maxEnd = 0, maxSum = LLONG_MIN;
for (int j = 0; j < m; j++) {
maxEnd = max(colSum[j], maxEnd + colSum[j]);
maxSum = max(maxSum, maxEnd);
}
ans = max(ans, maxSum);
}
}
return ans;
}
// --------------------------------------------
// 38) DP + Catalan Numbers
// --------------------------------------------
/*
* Compute n-th Catalan number using DP.
* - catalan: O(n^2) time, O(n) space.
* Catalan numbers grow fast; use long long for n ≤ 30.
*/
long long catalan(int n) {
vector<long long> cat(n + 1, 0);
cat[0] = 1;
for (int i = 1; i <= n; i++) {
for (int j = 0; j < i; j++) {
cat[i] += cat[j] * cat[i - j - 1];
}
}
return cat[n];
}
// --------------------------------------------
// 39) DP for "Partition into K subarrays with min/max sum"
// --------------------------------------------
/*
* Split array into k subarrays to minimize the maximum subarray sum.
* - canPartitionKSubarrays: greedy check for a given target.
* - splitArrayMinLargestSum: binary search on answer.
* Complexity O(n log sum).
*/
bool canPartitionKSubarrays(const vector<int>& nums, int k, long long target) {
int cnt = 1;
long long sum = 0;
for (int x : nums) {
if (sum + x > target) {
cnt++;
sum = x;
} else sum += x;
}
return cnt <= k;
}
long long splitArrayMinLargestSum(const vector<int>& nums, int k) {
long long lo = *max_element(nums.begin(), nums.end());
long long hi = accumulate(nums.begin(), nums.end(), 0LL);
long long ans = hi;
while (lo <= hi) {
long long mid = (lo + hi) / 2;
if (canPartitionKSubarrays(nums, k, mid)) {
ans = mid;
hi = mid - 1;
} else lo = mid + 1;
}
return ans;
}
// --------------------------------------------
// 40) DP + BFS/Shortest Path (count ways)
// --------------------------------------------
/*
* Counting ways in grid using BFS/DP (same as countPaths).
* - countWaysInGridWithBFS: just an alias.
*/
long long countWaysInGridWithBFS(int n, int m) {
vector<vector<long long>> dp(n, vector<long long>(m, 0));
dp[0][0] = 1;
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
if (i > 0) dp[i][j] += dp[i - 1][j];
if (j > 0) dp[i][j] += dp[i][j - 1];
}
}
return dp[n - 1][m - 1];
}
// --------------------------------------------
// 41) DP + Floyd-Warshall (all-pairs shortest paths)
// --------------------------------------------
/*
* All-pairs shortest paths in a graph (negative weights allowed, no negative cycles).
* - floydWarshall: O(n^3) time, O(n^2) space.
* Modifies dist matrix in-place.
*/
void floydWarshall(vector<vector<long long>>& dist) {
int n = dist.size();
for (int k = 0; k < n; k++) {
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
if (dist[i][k] != LLONG_MAX && dist[k][j] != LLONG_MAX) {
dist[i][j] = min(dist[i][j], dist[i][k] + dist[k][j]);
}
}
}
}
}
// --------------------------------------------
// 42) DP + Bellman-Ford (shortest path with negative weights)
// --------------------------------------------
/*
* Bellman-Ford for single-source shortest paths with negative edges.
* - bellmanFord: O(VE) time, O(V) space.
* Returns distances or empty vector if negative cycle detected.
*/
vector<long long> bellmanFord(int n, vector<tuple<int,int,long long>>& edges, int src) {
vector<long long> dist(n, LLONG_MAX);
dist[src] = 0;
for (int i = 0; i < n - 1; i++) {
for (auto [u, v, w] : edges) {
if (dist[u] != LLONG_MAX && dist[u] + w < dist[v]) {
dist[v] = dist[u] + w;
}
}
}
for (auto [u, v, w] : edges) {
if (dist[u] != LLONG_MAX && dist[u] + w < dist[v]) {
return {};
}
}
return dist;
}
// --------------------------------------------
// 43) DP + Grundy Numbers (impartial games)
// --------------------------------------------
/*
* Compute Grundy numbers for combinatorial games.
* - mex: minimum excluded value.
* - grundy: for a single pile with given moves.
* - grundyNumber: XOR of Grundy numbers for multiple piles.
*/
int mex(const vector<int>& v) {
vector<bool> vis(v.size() + 1, false);
for (int x : v) if (x < (int)vis.size()) vis[x] = true;
for (int i = 0; i < (int)vis.size(); i++) if (!vis[i]) return i;
return v.size();
}
int grundy(int n, vector<int>& moves) {
vector<int> g(n + 1, 0);
for (int i = 1; i <= n; i++) {
vector<int> reachable;
for (int m : moves) {
if (i >= m) reachable.push_back(g[i - m]);
}
g[i] = mex(reachable);
}
return g[n];
}
int grundyNumber(int piles[], int n, int maxMove) {
vector<int> g(maxMove + 1, 0);
for (int i = 1; i <= maxMove; i++) {
vector<int> reachable;
for (int move = 1; move <= i; move++) {
reachable.push_back(g[i - move]);
}
g[i] = mex(reachable);
}
int xr = 0;
for (int i = 0; i < n; i++) xr ^= g[piles[i]];
return xr; // zero means losing
}
// --------------------------------------------
// 44) DP + Convex Hull Trick (CHT)
// --------------------------------------------
/*
* Convex Hull Trick for DP optimization with linear functions.
* Slopes must be monotonic. Supports min queries.
* - Line: y = m*x + c.
* - CHT: add lines (monotonic slopes), query min at x.
* Use when DP transition is dp[i] = min_j (dp[j] + m_j * x_i + c_j).
*/
struct Line {
long long m, c;
long long eval(long long x) { return m * x + c; }
};
struct CHT {
vector<Line> lines;
int ptr = 0;
bool bad(Line l1, Line l2, Line l3) {
return (l3.c - l1.c) * (l1.m - l2.m) <= (l2.c - l1.c) * (l1.m - l3.m);
}
void addLine(Line l) {
while (lines.size() >= 2 && bad(lines[lines.size()-2], lines.back(), l))
lines.pop_back();
lines.push_back(l);
}
long long query(long long x) {
while (ptr + 1 < lines.size() && lines[ptr+1].eval(x) <= lines[ptr].eval(x))
ptr++;
return lines[ptr].eval(x);
}
};
// --------------------------------------------
// 45) DP + Li Chao Tree (for min/max queries)
// --------------------------------------------
/*
* Li Chao Tree for dynamic addition of lines and min/max queries over arbitrary x.
* Supports discrete x-coordinates.
* - LiChao: addLine, query.
*/
struct LiChao {
struct Node {
Line line;
Node *left, *right;
Node(Line l) : line(l), left(nullptr), right(nullptr) {}
};
Node* root;
long long xL, xR;
LiChao(long long l, long long r) : xL(l), xR(r), root(nullptr) {}
void addLine(Line nw) { root = addLine(root, xL, xR, nw); }
Node* addLine(Node* node, long long l, long long r, Line nw) {
if (!node) return new Node(nw);
long long mid = (l + r) / 2;
Line lo = node->line, hi = nw;
if (lo.eval(mid) > hi.eval(mid)) swap(node->line, nw);
if (l == r) return node;
if (nw.eval(l) < node->line.eval(l))
node->left = addLine(node->left, l, mid, nw);
else if (nw.eval(r) < node->line.eval(r))
node->right = addLine(node->right, mid+1, r, nw);
return node;
}
long long query(long long x) { return query(root, xL, xR, x); }
long long query(Node* node, long long l, long long r, long long x) {
if (!node) return LLONG_MAX;
long long res = node->line.eval(x);
if (l == r) return res;
long long mid = (l + r) / 2;
if (x <= mid) return min(res, query(node->left, l, mid, x));
else return min(res, query(node->right, mid+1, r, x));
}
};
// --------------------------------------------
// 46) DP + Alien Trick (Lagrangian relaxation)
// --------------------------------------------
/*
* Alien trick to solve DP with exactly K items.
* - solveWithPenalty: DP with penalty added per item.
* - solveWithExactlyK: binary search on penalty.
* Requires the DP to be convex.
* Placeholder implementation; fill with actual DP.
*/
pair<long long, int> solveWithPenalty(int penalty) {
// Placeholder: should return {max value, number of items}
return {0, 0};
}
long long solveWithExactlyK(int k) {
long long lo = -1e9, hi = 1e9, ans = 0;
while (lo <= hi) {
long long mid = (lo + hi) / 2;
auto [val, cnt] = solveWithPenalty(mid);
if (cnt >= k) {
ans = val + mid * k;
lo = mid + 1;
} else hi = mid - 1;
}
return ans;
}
// --------------------------------------------
// 47) DP + "Interval DP" (Burst Balloons, Palindrome cuts, etc.)
// --------------------------------------------
/*
* Interval DP examples.
* - maxCoinsBurstBalloons: Leetcode 312.
* - minCutPalindrome: minimum cuts to partition into palindromes.
* - longestValidParentheses: length of longest valid parentheses substring.
*/
int maxCoinsBurstBalloons(const vector<int>& nums) {
int n = nums.size();
vector<int> a(n + 2, 1);
for (int i = 1; i <= n; i++) a[i] = nums[i - 1];
vector<vector<int>> dp(n + 2, vector<int>(n + 2, 0));
for (int len = 1; len <= n; len++) {
for (int l = 1; l <= n - len + 1; l++) {
int r = l + len - 1;
for (int k = l; k <= r; k++) {
dp[l][r] = max(dp[l][r], dp[l][k - 1] + dp[k + 1][r] + a[l - 1] * a[k] * a[r + 1]);
}
}
}
return dp[1][n];
}
int minCutPalindrome(const string& s) {
int n = s.size();
vector<vector<bool>> isPal(n, vector<bool>(n, false));
for (int i = 0; i < n; i++) isPal[i][i] = true;
for (int len = 2; len <= n; len++) {
for (int i = 0; i <= n - len; i++) {
int j = i + len - 1;
if (s[i] == s[j] && (len == 2 || isPal[i + 1][j - 1])) {
isPal[i][j] = true;
}
}
}
vector<int> dp(n, INT_MAX);
for (int i = 0; i < n; i++) {
if (isPal[0][i]) dp[i] = 0;
else {
for (int j = 0; j < i; j++) {
if (isPal[j + 1][i] && dp[j] + 1 < dp[i]) {
dp[i] = dp[j] + 1;
}
}
}
}
return dp[n - 1];
}
int longestValidParentheses(const string& s) {
int n = s.size();
vector<int> dp(n, 0);
int ans = 0;
for (int i = 1; i < n; i++) {
if (s[i] == ')') {
if (s[i - 1] == '(') {
dp[i] = (i >= 2 ? dp[i - 2] : 0) + 2;
} else if (i - dp[i - 1] - 1 >= 0 && s[i - dp[i - 1] - 1] == '(') {
dp[i] = dp[i - 1] + 2 + (i - dp[i - 1] - 2 >= 0 ? dp[i - dp[i - 1] - 2] : 0);
}
ans = max(ans, dp[i]);
}
}
return ans;
}
// --------------------------------------------
// 48) DP + "Ugly Numbers" (DP with prime factors)
// --------------------------------------------
/*
* Find the n-th ugly number (factors only 2, 3, 5).
* - nthUglyNumber: O(n) time, O(n) space.
*/
long long nthUglyNumber(int n) {
vector<long long> ugly(n);
ugly[0] = 1;
int p2 = 0, p3 = 0, p5 = 0;
for (int i = 1; i < n; i++) {
long long next2 = ugly[p2] * 2;
long long next3 = ugly[p3] * 3;
long long next5 = ugly[p5] * 5;
long long next = min({next2, next3, next5});
ugly[i] = next;
if (next == next2) p2++;
if (next == next3) p3++;
if (next == next5) p5++;
}
return ugly[n - 1];
}
// --------------------------------------------
// 49) DP + "Shortest Common Supersequence"
// --------------------------------------------
/*
* Shortest string that contains both a and b as subsequences.
* - shortestCommonSupersequence: O(n*m) time and space.
*/
string shortestCommonSupersequence(const string& a, const string& b) {
int n = a.size(), m = b.size();
vector<vector<int>> dp(n+1, vector<int>(m+1, 0));
for (int i = 0; i <= n; i++) dp[i][0] = i;
for (int j = 0; j <= m; j++) dp[0][j] = j;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
if (a[i-1] == b[j-1]) dp[i][j] = dp[i-1][j-1] + 1;
else dp[i][j] = min(dp[i-1][j], dp[i][j-1]) + 1;
}
}
string res;
int i = n, j = m;
while (i > 0 || j > 0) {
if (i > 0 && j > 0 && a[i-1] == b[j-1]) {
res.push_back(a[i-1]); i--; j--;
} else if (i > 0 && (j == 0 || dp[i-1][j] <= dp[i][j-1])) {
res.push_back(a[i-1]); i--;
} else {
res.push_back(b[j-1]); j--;
}
}
reverse(res.begin(), res.end());
return res;
}
// --------------------------------------------
// 50) DP + Expected Steps in DAG
// --------------------------------------------
/*
* Expected number of steps to reach goal in a probabilistic DAG.
* - expectedSteps: assumes edges go forward, compute backwards.
*/
double expectedSteps(int n, vector<vector<double>>& trans) {
vector<double> dp(n, 0);
for (int u = n - 2; u >= 0; u--) {
double sum = 0;
for (int v = u + 1; v < n; v++) {
sum += trans[u][v] * (1 + dp[v]);
}
dp[u] = sum;
}
return dp[0];
}
// --------------------------------------------
// 51) DP + "Count Subsets with Sum ≤ Target" (MITM)
// --------------------------------------------
/*
* Count subsets with sum ≤ target using meet-in-the-middle.
* - countSubsetsWithSumLE: O(2^(n/2) log 2^(n/2)).
*/
long long countSubsetsWithSumLE(const vector<int>& arr, int target) {
int n = arr.size();
int n1 = n / 2, n2 = n - n1;
vector<int> L, R;
for (int mask = 0; mask < (1 << n1); mask++) {
int sum = 0;
for (int i = 0; i < n1; i++) if (mask & (1 << i)) sum += arr[i];
L.push_back(sum);
}
for (int mask = 0; mask < (1 << n2); mask++) {
int sum = 0;
for (int i = 0; i < n2; i++) if (mask & (1 << i)) sum += arr[n1 + i];
R.push_back(sum);
}
sort(R.begin(), R.end());
long long ans = 0;
for (int x : L) {
ans += upper_bound(R.begin(), R.end(), target - x) - R.begin();
}
return ans;
}
// --------------------------------------------
// 52) DP + "Zeta Transform" (SOS DP)
// --------------------------------------------
/*
* Zeta transform (subset sum) - same as SOS DP.
* - zetaTransform: in-place transform.
*/
vector<int> zetaTransform(vector<int> f) {
int n = f.size();
int bits = 0; while ((1 << bits) < n) bits++;
for (int i = 0; i < bits; i++) {
for (int mask = 0; mask < n; mask++) {
if (mask & (1 << i))
f[mask] += f[mask ^ (1 << i)];
}
}
return f;
}
// --------------------------------------------
// 53) DP + "LCS Optimized Space"
// --------------------------------------------
/*
* LCS with O(min(n,m)) space.
* - LCS_Optimized: swaps to make second string shorter.
*/
int LCS_Optimized(const string& a, const string& b) {
if (a.size() < b.size()) return LCS_Optimized(b, a);
int n = a.size(), m = b.size();
vector<int> dp(m + 1, 0), ndp(m + 1, 0);
for (int i = 1; i <= n; i++) {
ndp[0] = 0;
for (int j = 1; j <= m; j++) {
if (a[i-1] == b[j-1]) ndp[j] = dp[j-1] + 1;
else ndp[j] = max(dp[j], ndp[j-1]);
}
swap(dp, ndp);
}
return dp[m];
}
// --------------------------------------------
// 54) Utility Functions
// --------------------------------------------
/*
* Common utility functions for DP:
* - buildPrefix: 1D prefix sum.
* - rangeSum: sum in [l,r].
* - buildPrefix2D: 2D prefix sum.
* - rectSum: rectangle sum.
* - printDP: print 2D DP table (debugging).
* - resetMemo: reset memo array to -1.
*/
vector<long long> buildPrefix(const vector<long long>& arr) {
int n = arr.size();
vector<long long> pref(n + 1, 0);
for (int i = 0; i < n; i++) pref[i + 1] = pref[i] + arr[i];
return pref;
}
long long rangeSum(const vector<long long>& pref, int l, int r) {
return pref[r + 1] - pref[l];
}
vector<vector<long long>> buildPrefix2D(const vector<vector<long long>>& grid) {
int n = grid.size(), m = grid[0].size();
vector<vector<long long>> pref(n + 1, vector<long long>(m + 1, 0));
for (int i = 0; i < n; i++) {
for (int j = 0; j < m; j++) {
pref[i + 1][j + 1] = grid[i][j] + pref[i][j + 1] + pref[i + 1][j] - pref[i][j];
}
}
return pref;
}
long long rectSum(const vector<vector<long long>>& pref, int x1, int y1, int x2, int y2) {
return pref[x2 + 1][y2 + 1] - pref[x1][y2 + 1] - pref[x2 + 1][y1] + pref[x1][y1];
}
template<typename T>
void printDP(const vector<vector<T>>& dp) {
for (auto &row : dp) {
for (auto &val : row) cout << val << ' ';
cout << '\n';
}
}
void resetMemo(long long memo[], int size) {
for (int i = 0; i < size; i++) memo[i] = -1;
}
// --------------------------------------------
// 55) Additional: DP for "Can Partition K Subsets"
// --------------------------------------------
/*
* Check if array can be partitioned into k subsets with equal sum.
* - canPartitionKSubsets: bitmask DP, O(k * 2^n) or O(n * 2^n).
*/
bool canPartitionKSubsets(const vector<int>& nums, int k) {
int sum = accumulate(nums.begin(), nums.end(), 0);
if (sum % k != 0) return false;
int target = sum / k;
int n = nums.size();
vector<int> dp(1 << n, -1);
dp[0] = 0;
for (int mask = 0; mask < (1 << n); mask++) {
if (dp[mask] == -1) continue;
for (int i = 0; i < n; i++) {
if (!(mask & (1 << i)) && dp[mask] + nums[i] <= target) {
int newMask = mask | (1 << i);
int newSum = (dp[mask] + nums[i]) % target;
if (dp[newMask] == -1) dp[newMask] = newSum;
}
}
}
return dp[(1 << n) - 1] == 0;
}
// --------------------------------------------
// 56) DP + "Max Sum of Non-Adjacent Elements" (House Robber)
// --------------------------------------------
/*
* House Robber problems.
* - houseRobber: linear array.
* - houseRobberCircular: circular array (first and last adjacent).
*/
long long houseRobber(const vector<int>& nums) {
int n = nums.size();
if (n == 0) return 0;
if (n == 1) return nums[0];
vector<long long> dp(n, 0);
dp[0] = nums[0];
dp[1] = max(nums[0], nums[1]);
for (int i = 2; i < n; i++) {
dp[i] = max(dp[i - 1], dp[i - 2] + nums[i]);
}
return dp[n - 1];
}
long long houseRobberCircular(const vector<int>& nums) {
int n = nums.size();
if (n == 0) return 0;
if (n == 1) return nums[0];
vector<int> a(nums.begin(), nums.end() - 1);
vector<int> b(nums.begin() + 1, nums.end());
return max(houseRobber(a), houseRobber(b));
}
// --------------------------------------------
// 57) DP + "Distinct Subsequences" (count of t as subsequence of s)
// --------------------------------------------
/*
* Count distinct subsequences of s that equal t.
* - numDistinct: O(n*m) time and space.
*/
int numDistinct(const string& s, const string& t) {
int n = s.size(), m = t.size();
vector<vector<long long>> dp(n + 1, vector<long long>(m + 1, 0));
for (int i = 0; i <= n; i++) dp[i][0] = 1;
for (int i = 1; i <= n; i++) {
for (int j = 1; j <= m; j++) {
dp[i][j] = dp[i - 1][j];
if (s[i - 1] == t[j - 1]) dp[i][j] += dp[i - 1][j - 1];
}
}
return dp[n][m];
}
// =====================================================================
// EXAMPLE MAIN
// =====================================================================
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
// Example usage of some functions:
vector<int> arr = {10, 9, 2, 5, 3, 7, 101, 18};
cout << "LIS length = " << LIS(arr) << '\n';
vector<int> weight = {2, 3, 4, 5};
vector<int> value = {3, 4, 5, 6};
int W = 5;
cout << "Knapsack max value = " << knapsack01(weight, value, W) << '\n';
initComb(); // if using nCr
cout << "Fib(10) = " << fibOptimized(10) << '\n';
string s = "leetcode";
vector<string> dict = {"leet", "code"};
cout << "Word Break: " << wordBreak(s, dict) << '\n';
vector<int> houses = {2, 7, 9, 3, 1};
cout << "House Robber: " << houseRobber(houses) << '\n';
vector<int> dims = {40, 20, 30, 10, 30};
cout << "Matrix Chain Min Cost: " << matrixChainOrder(dims) << '\n';
string pal = "aab";
cout << "Min cuts for palindrome: " << minCutPalindrome(pal) << '\n';
cout << "10th ugly number: " << nthUglyNumber(10) << '\n';
return 0;
}