### **Bitonic Sequences** * **Time limit:** 200...
تم الإنشاء في: ٢٥ أكتوبر ٢٠٢٥
تمت الإجابة باستخدام GPT-5 Thinking بواسطة Chat01
تم الإنشاء في: ٢٥ أكتوبر ٢٠٢٥
تمت الإجابة باستخدام GPT-5 Thinking بواسطة Chat01
We define a positive bitonic sequence of sum N as a sequence of positive integers S₁, S₂, ..., Sₖ such that the sum of all elements equals N, and there exists an index j satisfying:
Find the number of distinct positive bitonic sequences with a total sum of N, for all 1 ≤ N ≤ M, modulo 10⁹ + 7.
Two bitonic sequences S₁ and S₂ are considered different if they have different lengths, or if there exists an index j' such that S₁,j' ≠ S₂,j'.
The first line of input contains a single integer M.
Print the number of distinct positive bitonic sequences for each N (1 ≤ N ≤ M), modulo 10⁹ + 7.
Input:
5
Output:
1 2 4 8 15
#include <bits/stdc++.h>
using namespace std;
const int MOD = 1000000007;
int addmod(int a, int b) { a += b; if (a >= MOD) a -= MOD; return a; }
int mulmod(long long a, long long b) { return int((a * b) % MOD); }
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
textint M; if (!(cin >> M)) return 0; vector<int> f(M + 1, 0); // Q_p coefficients up to degree T_p where needed. We maintain current Q array up to M. vector<int> Q(M + 1, 0), Qnext(M + 1, 0); Q[0] = 1; // Q_1 = 1, since product over v=1..p-1 empty // For p = 1, B_1(x) = x / (1 - x) = sum_{m >= 1} x^m // Contribution to f[N] from p=1 is A_1(N - m) with A_1(t) = coeff of Q_1(t) = [t == 0] // So f[N] += 1 for all N >= 1. for (int N = 1; N <= M; ++N) f[N] = addmod(f[N], 1); // Now iterate p from 2..M, build Q_p from Q_{p-1} by multiplying (1 - x^{p-1})^{-2} for (int p = 2; p <= M; ++p) { int k = p - 1; // Compute Qnext = Q * (1 - x^k)^{-2} truncated to degree M // For each residue r in [0..k-1], process indices t = r + i*k for (int r = 0; r < k; ++r) { // collect sequence a[i] = Q[r + i*k] for i s.t. idx <= M vector<int> a; for (int idx = r; idx <= M; idx += k) a.push_back(Q[idx]); int L = (int)a.size(); if (L == 0) continue; vector<long long> pref(L, 0), prefi(L, 0); pref[0] = a[0]; prefi[0] = 0LL; // 0 * a[0] for (int i = 1; i < L; ++i) { pref[i] = pref[i-1] + a[i]; if (pref[i] >= MOD) pref[i] -= MOD; prefi[i] = prefi[i-1] + 1LL * i * a[i]; prefi[i] %= MOD; } // b[i] = sum_{c=0..i} (c+1) * a[i-c] = (i+1) * sum_{j=0..i} a[j] - sum_{j=0..i} j * a[j] for (int i = 0; i < L; ++i) { long long sumA = pref[i]; long long sumJA = prefi[i]; long long val = ((long long)(i + 1) * sumA - sumJA) % MOD; if (val < 0) val += MOD; int idx = r + i * k; Qnext[idx] = (int)val; } } Q.swap(Qnext); // clear Qnext for next iteration fill(Qnext.begin(), Qnext.end(), 0); // Add contributions for this p: B_p(x) = sum_{c>=1} x^{pc} // For each N, f[N] += sum over m = p, 2p, ... ≤ N of A_p(N - m), where A_p = Q for (int m = p; m <= M; m += p) { int maxN = M; // for N from m..M: f[N] += Q[N - m] // We can do this as a loop; still O(M/p) per m, totals O(M log M) per p? Actually sum over m is M/p terms, each loop M - m ~ M, gives O(M^2/p) for (int N = m; N <= maxN; ++N) { f[N] = addmod(f[N], Q[N - m]); } } } // Output results for N=1..M for (int N = 1; N <= M; ++N) { if (N > 1) cout << ' '; cout << f[N]; } cout << '\n'; return 0;
}
I only passed a few use cases, and all the others timed out. How can I optimize this?
pythondef compositions(n): res=[] # generate compositions by splitting between positions of n ones def rec(rem, cur): if rem==0: res.append(cur.copy()) return for x in range(1, rem+1): cur.append(x) rec(rem-x, cur) cur.pop() rec(n, []) return res def is_unimodal(seq): k=len(seq) # j index 1..k for j in range(1, k+1): ok=True for i in range(1, j): if seq[i-1] > seq[i]: ok=False; break if not ok: continue for i in range(j, k): if seq[i-1] < seq[i]: ok=False; break if ok: return True return False for n in range(1,11): cnt=sum(1 for s in compositions(n) if is_unimodal(s)) print(n,cnt)
pythonseq=[1,2,4,8,15,27,47,79,130,209,336,538,857][:12] # find recurrence of small order using linear algebra mod integers def find_rec(seq, order=4): import numpy as np import fractions n=len(seq) best=None for k in range(1,7): # solve for coeffs c1..ck: s[n]=sum c_i s[n-i] A=[] b=[] for i in range(k, n): A.append(seq[i-1:i-k-1:-1]) b.append(seq[i]) import numpy.linalg as la try: x=la.lstsq(np.array(A,dtype=float), np.array(b,dtype=float), rcond=None)[0] # Round to integers maybe cand=[round(v) for v in x] ok=True for i in range(k, n): pred=sum(cand[j]*seq[i-1-j] for j in range(k)) if
python# compute partitions into odd parts (equals partitions into distinct parts) from functools import lru_cache def partitions_odd(n): @lru_cache(None) def dp(i, rem): # i odd upto rem if rem==0: return 1 if i>rem: return 0 return dp(i, rem-i) + dp(i+2, rem) return dp(1, n) def partitions(n): @lru_cache(None) def dp(maxp, rem): if rem==0: return 1 if maxp==0: return 0 if maxp>rem: return dp(rem, rem) res=0 for k in range(rem//maxp+1): res+=dp(maxp-1, rem-k*maxp) return res return dp(n,n) vals=[(n, partitions_odd(n), partitions(n)) for n in range(1,11)] vals [(1, 1, 1), (2, 1, 2), (3, 2, 3), (4, 2, 5), (5, 3, 7), (6, 4, 11), (7, 5, 15), (8, 6, 22), (9, 8, 30), (10, 10, 42)]
You’re running into the right idea (use the GF)
but the way you add each -block is overall (and your update of is also per , so total). With that will time out.
Below are two optimizations, in order of impact.
Key observation: define the (truncated) polynomials
Then
Instead of forming each prefix product and convolving it with one-by-one, build a segment tree over and compute everything with polynomial multiplications (each is NTT/CRT-based, degrees trimmed to ):
Every internal node performs one polynomial multiplication for the product tree and one for the D-tree, both truncated to degree . With an NTT this is total and is easily fast enough.
Implementation tips:
poly[0]+=1, poly[k]+=2, poly[2k]+=3, … until degree .poly[cp]=1 for cp in {p,2p,… ≤ M}.This completely eliminates your loops; you never form explicitly and you never add “multiples of ” with nested loops—the tree does the algebra for all at once.
Sketch (only the structure; drop-in NTT is standard and omitted for brevity):
cpp// Build leaves H_k and G_p up to degree M. // Product tree: P[node] = P[left]*P[right] (truncate to deg M) // D-tree: D[node] = D[left] + P[left] * D[right] (truncate to deg M) // Answer poly = D[root]; print coeffs 1..M. struct Poly { vector<int> a; }; // coeffs mod 1e9+7 via CRT in mul() Poly mul(const Poly& A, const Poly& B, int M); // NTT+CRT, truncate to M void add_inplace(Poly& A, const Poly& B); // (mod 1e9+7), resize as needed void trim(Poly& A, int M); vector<Poly> Hleaf(M+1), Gleaf(M+1); void build_H_G(int M) { for (int k = 1; k <= M; ++k) { auto& H = Hleaf[k].a; H.assign(M+1, 0); for (int t = 0, deg = 0; deg <= M; ++t, deg = t*k) { if (deg > M) break; H[deg] = (H[deg] + (t+1)) % MOD; // (1 - x^k)^{-2} } auto& G = Gleaf[k].a; G.assign(M+1, 0); for (int deg = k; deg <= M; deg += k) G[deg] = 1; // x^k/(1-x^k) } } struct Node { Poly P, D; }; vector<Node> seg(4*(M+5)); void build(int idx, int L, int R, int M) { if (L == R) { seg[idx].P.a = Hleaf[L].a; trim(seg[idx].P, M); seg[idx].D.a = Gleaf[L].a; trim(seg[idx].D, M); return; } int mid = (L+R)>>1; build(idx<<1, L, mid, M); build(idx<<1|1, mid+1, R, M); seg[idx].P = mul(seg[idx<<1].P, seg[idx<<1|1].P, M); Poly rightWeighted = mul(seg[idx<<1].P, seg[idx<<1|1].D, M); seg[idx].D = seg[idx<<1].D; add_inplace(seg[idx].D, rightWeighted); trim(seg[idx].D, M); }
After build(1,1,M,M), seg[1].D.a[n] (for 1 ≤ n ≤ M) are your answers.
Right now you do:
cppfor (int m = p; m <= M; m += p) for (int N = m; N <= M; ++N) f[N] += Q[N - m];
Rewrite that using residue-class prefix sums (for each r in [0..p-1]):
a[i] = Q[r + i*p] for i ≥ 0,pref[i] = pref[i-1] + a[i],j ≥ 1, add pref[j-1] to f[r + j*p].This makes the B_p update (down from per ), but the whole algorithm remains and will still TLE at the given limit. Use it only as an incremental improvement if you can’t implement #1 yet.
If you want, I can share a full drop-in NTT+CRT template wired to the tree above; it’s a bit long for a single message, but that’s the exact code pattern to make this pass.