import java.io.*; import java.util.*; public clas...
생성일: 2025년 7월 5일
답변에 사용된 모델 O4 Mini High by Chat01
생성일: 2025년 7월 5일
답변에 사용된 모델 O4 Mini High by Chat01
import java.io.;
import java.util.;
public class Main {
static int n, m;
static long total; // sum of f(a) over all sequences
textpublic static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); int T = Integer.parseInt(br.readLine().trim()); StringBuilder sb = new StringBuilder(); while (T-- > 0) { StringTokenizer st = new StringTokenizer(br.readLine()); n = Integer.parseInt(st.nextToken()); m = Integer.parseInt(st.nextToken()); total = 0; // a[1..n], 1-based for convenience int[] a = new int[n+1]; genSequence(1, a); sb.append(total % m).append('\n'); } System.out.print(sb); } // Recursively build a[1..n] static void genSequence(int i, int[] a) { if (i > n) { // we have a full valid sequence; compute f(a) List<Integer> tokens = new ArrayList<>(); for (int t = 1; t <= n; t++) tokens.add(t); long fa = countRemovals(1, a, tokens); total = (total + fa) % m; return; } // choose a[i] in [0..i] for (int v = 0; v <= i; v++) { a[i] = v; genSequence(i+1, a); } } // Recursively count removal ways for sequence a, starting at step i, // with 'tokens' currently still present. static long countRemovals(int i, int[] a, List<Integer> tokens) { if (i > n) { // performed all removals return 1; } if (a[i] == 0) { // no removal this step return countRemovals(i+1, a, tokens); } long ways = 0; // collect all eligible token positions t in [a[i]..i] for (int idx = 0; idx < tokens.size(); idx++) { int t = tokens.get(idx); if (t >= a[i] && t <= i) { // remove t tokens.remove(idx); ways = (ways + countRemovals(i+1, a, tokens)) % m; // backtrack tokens.add(idx, t); } } return ways; }
}
optimize this
python# Let's brute compute total for small n without mod m (m large). from functools import lru_cache def compute_total(n): # compute total sum f(a) over sequences total = 0 # generate all sequences a[1..n] with a[i] in 0..i # recursion def gen(i, a): nonlocal total if i > n: # compute f(a) # tokens from 1..n # count removals total += count(1, tuple(a)) return for v in range(i+1): # 0..i a.append(v) gen(i+1, a) a.pop() @lru_cache(None) def count(i, a, tokens=None): # tokens represented as tuple if tokens is None: tokens = tuple(range(1, n+1)) if i > n: return 1 ai = a[i-1] if ai == 0: return count(i+1, a, tokens) ways = 0 # for each eligible token new_tokens = list(tokens) for idx, t in enumerate(tokens): if ai <= t <= i: # remove t nt = list(tokens) nt.pop(idx) ways += count(i+1, a, tuple(nt)) return ways gen(1, []) return total for n in range(1,7): print(n, compute_total(n))
Here’s a purely recursive solution that collapses the two‐pass approach (first over all a, then over all removal orders) into one, and which avoids all of the costly List
creations and index‐shifts:
javaimport java.io.*; import java.util.*; public class Main { static int n, m; static long total; // tokens[k] = the k-th token still present; we keep tokenCount of them static int[] tokens; public static void main(String[] args) throws IOException { BufferedReader br = new BufferedReader(new InputStreamReader(System.in)); int T = Integer.parseInt(br.readLine().trim()); StringBuilder sb = new StringBuilder(); while (T-- > 0) { StringTokenizer st = new StringTokenizer(br.readLine()); n = Integer.parseInt(st.nextToken()); m = Integer.parseInt(st.nextToken()); // initialize total = 0; tokens = new int[n]; for (int i = 0; i < n; i++) tokens[i] = i + 1; // start at step 1, with product = 1 and all n tokens present dfs(1, 1L, n); sb.append(total).append('\n'); } System.out.print(sb); } /** * @param step current removal step (1..n) * @param prod product of all previously chosen t's (i.e. number of choices * for a[?] so far), mod m * @param tokenCount how many tokens remain in tokens[0..tokenCount-1] */ static void dfs(int step, long prod, int tokenCount) { if (step > n) { // we've processed all steps → accumulate one full (a+removal) count total = (total + prod) % m; return; } // 1) choose a[step] = 0 (no removal this step) dfs(step + 1, prod, tokenCount); // 2) removal: pick any token ≤ step // and for each t we also have t different a[step]-values (1..t) for (int j = 0; j < tokenCount; j++) { int t = tokens[j]; if (t <= step) { // remove tokens[j] by swapping it out of the active prefix tokens[j] = tokens[tokenCount - 1]; dfs(step + 1, (prod * t) % m, tokenCount - 1); // restore tokens[j] = t; } } } }
Single recursive pass
Instead of first iterating all sequences a[1..n]
and then, for each, recursing again over removal-orders, we do both at once. At step i we either:
a[i]=0
case), ort
and account for the fact there are t
possible lower‐bounds a[i]=1..t
by multiplying our running product by t
.This removes an entire factor of n in the branching and cuts out the inner list recursion altogether.
Primitive arrays + in-place swaps
int[] tokens
and an integer tokenCount
.tokenCount
, then swap it back on return.new ArrayList
, no remove(idx)
(which is O(n)), and no boxing/unboxing of Integer
.Early modulo
We do (prod * t) % m
on each step, and %m
on every addition to total
, so we never let our intermediate values overflow or blow up.
With these changes you’ve eliminated all of the list‐copying and double‐recursion overhead and pushed the constant factor down dramatically.