ограничение по времени на тест 2 секунды ограничен...

創建於:2026年4月7日

使用 GPT-5.4 Thinking 回答 Chat01

問題

ограничение по времени на тест 2 секунды
ограничение по памяти на тест 256 мегабайт
Рассмотрим бинарные деревья, в которых у каждой вершины не более двух детей и каждая вершина помечена либо 0, либо 1.

За одну операцию можно инвертировать все метки на любом простом пути, проходящем через корень. Инвертировать метку означает заменить 0 на 1, а 1 на 0. Заметьте, что путь длины 1, содержащий только корень, тоже разрешён.

Стоимость такого бинарного дерева определяется как минимальное число операций, необходимое для того, чтобы сделать все метки в дереве равными 0.

Даны два целых числа 𝑛 и 𝑘. Посчитайте количество различных бинарных деревьев ровно с 𝑛 вершинами и стоимостью ровно 𝑘. Так как ответ может быть очень большим, выведите его по модулю 10^9+7.

Два бинарных дерева считаются различными, если у них различается структура, либо если структура одинакова, но хотя бы в одной вершине различаются метки.

Входные данные
Каждый тест состоит из нескольких наборов входных данных. В первой строке находится одно целое число 𝑡 (1≤𝑡≤10^4) — количество наборов входных данных. Далее следует описание наборов входных данных.

В единственной строке каждого набора входных данных содержатся два целых числа 𝑛 и 𝑘 (1≤𝑛≤10^6, 0≤𝑘≤𝑛).

Гарантируется, что сумма значений 𝑛 по всем наборам входных данных не превосходит 10^6.

Выходные данные
Для каждого набора входных данных выведите на отдельной строке одно целое число — количество помеченных бинарных деревьев с 𝑛 вершинами и стоимостью 𝑘 по модулю 10^9+7.

Пример
входные данныеСкопировать
6
2 0
2 1
1 1
7 6
69 67
314159 42
выходные данныеСкопировать
2
4
1
2016
382216284
559671824
Примечание
В первом наборе входных данных имеем 𝑛=2 и 𝑘=0. Существует два возможных дерева — корень с левым ребёнком и корень с правым ребёнком. Поскольку нужна стоимость 0, все вершины должны быть помечены 0. Следовательно, ответ равен 2.

Во втором наборе входных данных имеем 𝑛=2 и 𝑘=1. Таких деревьев 4. Допустимые помеченные деревья показаны на рисунке ниже:

В третьем наборе входных данных имеем 𝑛=1 и 𝑘=1. Существует только одно дерево — одна корневая вершина с меткой 1. Следовательно, ответ равен 1.

Реши на с++

I'm testing if A_nk follows a form involving Catalan numbers or powers of 2. I’ve noticed that for k=1, A_n1 seems to follow the pattern 4^(n-1). For k=2, the values grow rapidly, and I wonder if there's a binomial connection. Maybe I can derive a recurrence or check OEIS for hints.{0, 1}.

I’m trying to figure out if A[n,n] follows a pattern similar to Catalan numbers. The pattern I see seems to match twice the Catalan sequence for most values: 2 * Catalan_{n-1}. I’ll need to test this hypothesis more systematically and potentially derive it from generating functions and Lagrange’s formula.ves and binomial coefficients. I need to verify if this works for all k.bash -lc python3 - <<'PY' import sympy as s z=s.symbols('z') C=(1-s.sqrt(1-4z))/(2z) q=C-1 for k in [1,2,6]: S=lambda m: (1-q**(m+1))/s.sqrt(1-4z) if m>=0 else 0 T=s.expand(z(s.simplify(S(k)2 - S(k-2)2))) series=s.series(T,z,0,10) print('k',k,series) Tq=s.simplify((1+q)/(1-q)(2qk - q(2k-1)-q**(2k+1))) print('equal?', s.simplify(T-Tq)==0) PY

Revisiting recurrence formula and coefficients

Hmm, it seems like the series from T_k isn’t matching the data from brute force calculations. For k=1 and n=7, I got 2002 instead of 4096. When I check my T_k formula, it seems connected to a different sequence, maybe D(k) instead of A. I’ll revisit my recurrence, as my earlier formula seems off for A_nk. Let me recheck.

Aha, it seems the S1 formula is giving Catalans, not cumulative. The formula might be correct for the ratio, but I think I misspoke regarding the relationship between q and z C^2. The sum I used still needs some adjustments since the resulting series is off by one. Let's refine the derivation, especially for different values of m and n, to make sure we're counting correctly.

MOD=10**9+7
from functools import lru_cache
def cat(n):
import math
from math import comb
return comb(2n,n)//(n+1)
def A(n,k):
B=[[0]
(n+1) for _ in range(n+1)]
C=[[0]*(n+1) for _ in range(n+1)]
# B one-arm
B[0][0]=1
for m in range(1,n+1):
for i in range(m):
j=m-1-i
for a in range(i+1):
if B[i][a]==0: continue
for b in range(j+1):
if B[j][b]==0: continue
s=a+b
B[m][s]+=B[i][a]*B[j][b]
B[m][s+1]+=B[i][a]*B[j][b]
C[0][0]=1
for m in range(1,n+1):
for i in range(m):
j=m-1-i
for a in range(i+1):
if B[i][a]==0: continue
for b in range(j+1):
if B[j][b]==0: continue
t=max(a,b)
C[m][t]+=B[i][a]*B[j][b]
C[m][t+1]+=B[i][a]*B[j][b]
return C[n][k],B[n]
for n in range(1,8):
vals=[]
for k in range(n+1):
vals.append(A(n,k)[0])
print(n, vals, 'cat',cat(n), 'B?',A(n,min(n,0))[1][:n+1])

Нашёл удобную промежуточную модель.

Есть две похожие “стоимости”:

если можно инвертировать только путь от корня к одной вершине, это даёт очень простую биномиальную формулу;
исходная задача получается из неё через свёртку по левому и правому поддереву.
Сейчас дожимаю эту свёртку до закрытой формулы/быстрого DP.bash -lc python3 - <<'PY' from math import comb

def compute(nmax): B=[0 for _ in range(nmax+1)] A=[0 for _ in range(nmax+1)] B[0][0]=1 A[0][0]=1 for n in range(1,nmax+1): for i in range(n): j=n-1-i for a,v1 in enumerate(B[i]): if not v1: continue for b,v2 in enumerate(B[j]): if not v2: continue s=a+b B[n][s]+=v1v2 B[n][s+1]+=v1v2 t=max(a,b) A[n][t]+=v1v2 A[n][t+1]+=v1v2 return A,B A,B=compute(8) for n in range(1,8): print('n',n,'A',A[n][:n+1]) print('n',n,'B',B[n][:n+1]) PY

Simplifying generating function expression

I'm realizing the expression is a bit messy, but simplifying the square root gives a clearer form:
1

q
1
+
q
1+q
1−q

. Now, I'll substitute this back into the equation. For
a

1
a≥1, the formula might involve a Catalan polynomial, so I'll simplify it manually to see if I can spot any patterns.bash -lc python3 - <<'PY' import sympy as s q,u=s.symbols('q u') z=q/(1+q)*2 srt=(1-q)/(1+q) H=(1-srts.sqrt(1-4qu/(1-q)**2))/(2q(1+u)/(1+q)**2) # maybe not exact

let's just substitute sqrt simplification in previous H

H=(1-((1-q)/(1+q))s.sqrt(1-4qu/(1-q)*2))/(2q(1+u)/(1+q)**2) H=s.simplify(H) print('H=',H) for a in range(5): Ea=s.simplify(s.series(H,u,0,a+1).removeO().coeff(u,a)) print('a',a, s.factor(Ea)) PY

Working on generating function and pattern recognition

So, I have this formula for
E
a
(
q
)
E
a

(q), with some expressions for small values of
a
a, but the polynomials might relate to Narayana/Catalan numbers — not sure if it's needed though. Let's focus on simplifying the inner sum and computing pair counts. I’ll look at generating functions
J
a
,
b
(
z
)
J
a,b

(z) and explore how to connect them to simpler forms with polynomials and powers of
(
1
+
q
)
(1+q) and
(
1

q
)
(1−q). Let me test a few small
k
k values for clues.bash -lc python3 - <<'PY' import sympy as s q,u=s.symbols('q u') z=q/(1+q)**2 H=(1-s.sqrt(1-4z(1+u)))/(2z(1+u)) for k in range(5): G=s.simplify(sum(s.series(H,u,0,k+1).removeO().coeff(u,a) for a in range(k+1))) print('k',k, s.factor(G)) PY

derive Gk by solving from known z-series maybe? Use H expansion but substitute sqrt=(1-q)/(1+q)

let's directly use formulas from E_a simplified and sum.

E=[1+q, q*(1+q)/(1-q), q2*(2-q)(1+q)/(1-q)3, q3(q2-4q+5)(1+q)/(1-q)**5] for k in range(4): G=sum(E[:k+1]) print('G',k,s.factor(s.together(G))) A2=s.simplify(q/(1+q)**2 * (G.subs(k,2) if False else 0)) PY

I’m considering deriving the generating function for
A
k
A
k

directly through Lagrange’s method or by interpreting labeled tree counts via Schröder/ballot numbers. I’ll try using recurrence on tree counts and an exact bivariate generating function to avoid needing a closed form for
G
k
G
k

. Let’s investigate further.

思考

Сначала быстро проверю, нет ли подходящего встроенного навыка для задач по алгоритмам, а затем добью вывод и дам готовое решение на C++.

Thought for 11s

Python

python
from functools import cache @cache def all_shapes(n): if n==0:return [None] res=[] for i in range(n): for L in all_shapes(i): for R in all_shapes(n-1-i): res.append((L,R)) return tuple(res) def traverse(shape): arr=[] def dfs(s): if s is None:return None idx=len(arr); arr.append([None,None]) L,R=s li=dfs(L); ri=dfs(R) arr[idx]=[li,ri] return idx dfs(shape) return arr def ops(shape): tr=traverse(shape) n=len(tr) # vertices on left subtree and right subtree? parent=[-1]*n for i,(l,r) in enumerate(tr):

Python

python
from math import comb def rec(nmax): B=[[0]*(nmax+1) for _ in range(nmax+1)] A=[[0]*(nmax+1) for _ in range(nmax+1)] B[0][0]=1 A[0][0]=1 for n in range(1,nmax+1): for i in range(n): j=n-1-i for a in range(i+1): if not B[i][a]: continue for b in range(j+1): if not B[j][b]: continue s=a+b B[n][s]+=B[i][a]*B[j][b] B[n][s+1]+=B[i][a]*B[j][b] t=max(a,b) A[n][t]+=B[i][a]*B[j][b] A[n][t+1]+=B[i][a]*B[j][b] return A,B A,B=rec(

Python

python
import sympy as s z=s.symbols('z') C=(1-s.sqrt(1-4*z))/(2*z) q=s.simplify(C-1) def S_formula(m): return (1-q**(m+1))/s.sqrt(1-4*z) for m in range(4): ser=s.series(S_formula(m), z, 0, 8).removeO() coeff=[s.expand(ser).coeff(z,n) for n in range(8)] print("m",m,coeff) coeff2=[] for n in range(8): coeff2.append(sum(s.binomial(n,a)*s.catalan(n) for a in range(m+1))) print(coeff2)

Python

python
A,B=rec(8) for n in range(1,8): print(n,A[n][:n+1])

Python

python
from math import comb for r in range(0,5): vals=[] for n in range(max(1,r),8): if n>=r: vals.append((n,A[n][n-r])) print("r",r,vals)

Python

python
import sympy as s q,u=s.symbols('q u') z=q/(1+q)**2 H=s.simplify((1-s.sqrt(1-4*z*(1+u)))/(2*z*(1+u))) print(H) for a in range(6): Ea=s.simplify(s.expand(s.series(H,u,0,a+1).removeO()).coeff(u,a)) print(a,s.factor(s.together(Ea)))

Python

python
q,u=s.symbols('q u') z=q/(1+q)**2 H=s.simplify((1-s.sqrt(1-4*z*(1+u)))/(2*z*(1+u))) H=s.simplify(H.subs(s.sqrt((q-1)**2),1-q).subs(s.sqrt(q**2-2*q+1),1-q)) for a in range(6): Ea=s.simplify(s.expand(s.series(H,u,0,a+1).removeO()).coeff(u,a)) Ea=s.factor(s.together(Ea)) print(a,Ea)

Python

python
import sympy as s q=s.symbols('q') z=q/(1+q)**2 # compute S_k as sum_n Catalan_n * prefix * z^n; then rewrite in q via series and maybe rational_interpolate def catalan(n): from math import comb return comb(2*n,n)//(n+1) for k in range(6): N=20 ser=sum(sum(s.binomial(n,a) for a in range(k+1))*catalan(n)*z**n for n in range(N)) # simplify rationally guess with denominator (1-q)**(2*k+1) expr=s.simplify(s.expand(ser)) # use sympy simplify to rational function maybe rat=s.apart(s.simplify(s.together(expr)),q) print("k",k, s.factor(s.together(s.simplify(expr))))

Python

python
from math import comb def cat(n): return comb(2*n,n)//(n+1) # series q(z) coefficients Catalan n>=1 N=20 qcoef=[0]+[cat(n) for n in range(1,N)] # q = sum_{n>=1} C_n z^n def series_add(a,b,N): c=[0]*N for i in range(N): c[i]=(a[i] if i<len(a) else 0)+(b[i] if i<len(b) else 0) return c def series_mul(a,b,N): c=[0]*N for i,x in enumerate(a): if x==0: continue for j,y in enumerate(b): if i+j>=N: break c[i+j]+=x*y return c def series_pow_one_minus_inv(exp,N): # (1-q)^(-exp) q=[0]*N for i in range(1,N): q[i]=cat(i) res=[0]*N; res[0]=1 # use binomial sum in q powers term=[0]*N; term[0]=1 for m in range(1,N): term=series_mul(term,q,N) coeff=comb(exp+m-1,m) for i in range(N): res[i]+=coeff*term[i] return res def q_series_of_poly(poly,N): # list coeff by q^d res=[0]*N; res[0]=poly[0] if poly else 0 q=[0]*N for i in range(1,N): q[i]=cat(i) powq=[[0]*N for _ in range(len(poly))] powq[0][0]=1 for d in range(1,len(poly)): powq[d]=series_mul(powq[d-1],q,N) for d,c in enumerate(poly): for i in range(N): res[i]+=c*powq[d][i] return res def conv_to_q_numerator(target,k,N=12): den=series_pow_one_minus_inv(2*k+1,N) # Find polynomial num of degree maybe k or 2k? maxdeg=2*k+2 # solve linear system using first maxdeg+1 coefficients import fractions, math # Build powers of q times den basis=[] q=[0]*N for i in range(1,N): q[i]=cat(i) cur=[0]*N; cur[0]=1 for d in range(maxdeg+1): basis.append(series_mul(cur,den,N)) cur=series_mul(cur,q,N) # solve by exact rationals using gaussian M=maxdeg+1 A=[[basis[d][n] for d in range(M)] for n in range(M)] b=[target[n] for n in range(M)] # gauss from fractions import Fraction A=[[Fraction(x) for x in row] for row in A]; b=[Fraction(x) for x in b] r=0 for c in range(M): piv=None for i in range(r,M): if A[i][c]: piv=i; break if piv is None: continue A[r],A[piv]=A[piv],A[r]; b[r],b[piv]=b[piv],b[r] fac=A[r][c] A[r]=[x/fac for x in A[r]]; b[r]/=fac for i in range(M): if i!=r and A[i][c]: fac=A[i][c] A[i]=[A[i][j]-fac*A[r][j] for j in range(M)] b[i]=b[i]-fac*b[r] r+=1 coeff=[b[i] for i in range(M)] return coeff for k in range(4): target=[sum(comb(n,a) for a in range(k+1))*cat(n) for n in range(10)] coeff=conv_to_q_numerator(target,k,12) print("k",k,coeff)

Python

python
import sympy as s q=s.symbols('q') nums = { 0:[1,0,-1], 1:[1,-1,-1,1], 2:[1,-3,4,-1,-4,4,-1], 3:[1,-5,11,-7,-7,11,-5,1] } for k,arr in nums.items(): poly=sum(arr[i]*q**i for i in range(len(arr))) print(k,s.factor(poly))

Python

python
from math import comb def series_from_coeffs(coeffs,N): res=[0]*N for n,c in enumerate(coeffs[:N]): res[n]=c return res # compute z-series target for F_k def compute_A_prefix(k,N): arr=[0]*N for n in range(1,N): arr[n]=A[n][k] if n<len(A) and k<=n else 0 return arr # Need A bigger A,B=rec(20) def conv_to_q_rational(target,den_exp,N=15,maxdeg=None): if maxdeg is None:maxdeg=den_exp+4 den=series_pow_one_minus_inv(den_exp,N) q=[0]*N for i in range(1,N): q[i]=cat(i) basis=[] cur=[0]*N; cur[0]=1 for d in range(maxdeg+1): basis.append(series_mul(cur,den,N)) cur=series_mul(cur,q,N) M=maxdeg+1 # solve using first M equations from fractions import Fraction AA=[[Fraction(basis[d][n]) for d in range(M)] for n in range(M)] bb=[Fraction(target[n]) for n in range(M)] r=0 pivcols=[] for c in range(M): piv=None for i in range(r,M): if AA[i][c]: piv=i; break if piv is None: continue AA[r],AA[piv]=AA[piv],AA[r]; bb[r],bb[piv]=bb[piv],bb[r] fac=AA[r][c] AA[r]=[x/fac for x in AA[r]]; bb[r]/=fac for i in range(M): if i!=r and AA[i][c]: fac=AA[i][c] AA[i]=[AA[i][j]-fac*AA[r][j] for j in range(M)] bb[i]=bb[i]-fac*bb[r] pivcols.append(c) r+=1 coeff=[Fraction(0) for _ in range(M)] for i,c in enumerate(pivcols): coeff[c]=bb[i] return coeff for k in range(1,6): target=[0]+[A[n][k] for n in range(1,15)] # guess denominator (1-q)^(2k?) coeff=conv_to_q_rational(target,2*k+1,N=18,maxdeg=2*k+3) poly=sum(coeff[i]*q**i for i in range(len(coeff))) print("k",k, s.factor(poly))

Python

python
# test q-form guesses def q_to_z_coeff(expr,N=8): # expr sympy in q, substitute q series recursively? easiest numerically by z expression z=s.symbols('z') q_expr=(1-s.sqrt(1-4*z)-2*z)/(2*z) # maybe C-1 ser=s.series(s.simplify(expr.subs(q,q_expr)),z,0,N).removeO() return [s.expand(ser).coeff(z,n) for n in range(N)] for expr in [q**2*(1+q)*(2-q)/(1-q)**3, q**3*(1+q)*(5-4*q+q**2)/(1-q)**5]: print(q_to_z_coeff(expr,8))

Python

python
q=s.symbols('q') E0=1+q E1=q*(1+q)/(1-q) E2=q**2*(1+q)*(2-q)/(1-q)**3 for k,expr in [(1,E0+E1),(2,E0+E1+E2),(3,E0+E1+E2+q**3*(1+q)*(5-4*q+q**2)/(1-q)**5)]: print(k,s.factor(s.together(expr)))

Python

python
import sympy as s z=s.symbols('z') qexpr=(1-s.sqrt(1-4*z)-2*z)/(2*z) def coeffs_from_q(expr,N=10): ser=s.series(s.simplify(expr.subs(q,qexpr)),z,0,N).removeO() return [s.expand(ser).coeff(z,n) for n in range(N)] for expr in [q**2*(2-q)/(1-q)**4, q**2*(2+q)/(1-q)**4, q**2*(1+q)/(1-q)**4, q**2*(1+q)**2/(1-q)**4]: print(expr, coeffs_from_q(expr,8))

Python

python
from math import comb def Aseq(k,N): return [0]+[A[n][k] if k<=n else 0 for n in range(1,N)] # Let's fit numerator degree maybe k+1 or 2k-1 for denom (1-q)^(2k) def fit_q(target,den_exp,maxdeg,N=20): den=series_pow_one_minus_inv(den_exp,N) qser=[0]*N for i in range(1,N): qser[i]=cat(i) basis=[] cur=[0]*N; cur[0]=1 for d in range(maxdeg+1): basis.append(series_mul(cur,den,N)) cur=series_mul(cur,qser,N) M=maxdeg+1 # least exact solve first M coeffs from fractions import Fraction AA=[[Fraction(basis[d][n]) for d in range(M)] for n in range(M)] bb=[Fraction

Python

python
from math import comb def rec(nmax): B=[[0]*(nmax+1) for _ in range(nmax+1)] A=[[0]*(nmax+1) for _ in range(nmax+1)] B[0][0]=1 A[0][0]=1 for n in range(1,nmax+1): for i in range(n): j=n-1-i for a in range(i+1): if not B[i][a]: continue for b in range(j+1): if not B[j][b]: continue val=B[i][a]*B[j][b] s=a+b B[n][s]+=val B[n][s+1]+=val t=max(a,b) A[n][t]+=val A[n][t+1]+=val return A,B A,B=rec(30) def cat(n): return comb(2*n,n)//(n+1) N=60 qcoef=[0]+[cat(n) for n in range(1,N)] # q(z) def mul(a,b,N): c=[0]*N for i,x in enumerate(a): if not x: continue for j,y in enumerate(b): if i+j>=N: break c[i+j]+=x*y return c def one_minus_inv(p,N): q=[0]*N for i in range(1,N): q[i]=cat(i) res=[0]*N; res[0]=1 term=[0]*N; term[0]=1 for m in range(1,N): term=mul(term,q,N) coeff=comb(p+m-1,m) for i in range(N): res[i]+=coeff*term[i] return res from fractions import Fraction def fit(target, den_exp, maxdeg, N=40): den=one_minus_inv(den_exp,N) q=[0]*N for i in range(1,N): q[i]=cat(i) basis=[] cur=[0]*N; cur[0]=1 for d in range(maxdeg+1): basis.append(mul(cur,den,N)) cur=mul(cur,q,N) M=maxdeg+1 AA=[[Fraction(basis[d][n]) for d in range(M)] for n in range(M)] bb=[Fraction(target[n]) for n in range(M)] r=0; piv=[] for c in range(M): pv=None for i in range(r,M): if AA[i][c]: pv=i; break if pv is None: continue AA[r],AA[pv]=AA[pv],AA[r]; bb[r],bb[pv]=bb[pv],bb[r] fac=AA[r][c] AA[r]=[x/fac for x in AA[r]]; bb[r]/=fac for i in range(M): if i!=r and AA[i][c]: fac=AA[i][c] AA[i]=[AA[i][j]-fac*AA[r][j] for j in range(M)] bb[i]=bb[i]-fac*bb[r] piv.append(c); r+=1 coeff=[Fraction(0) for _ in range(M)] for i,c in enumerate(piv): coeff[c]=bb[i] # verify out=[Fraction(0)]*N for d,c in enumerate(coeff): if not c: continue for n in range(N): out[n]+=c*basis[d][n] ok=all(out[n]==target[n] for n in range(min(N,len(target)))) return coeff,ok import sympy as s q=s.symbols('q') for k in range(1,8): target=[0]*40 for n in range(1,40): target[n]=A[n][k] if n<len(A) and k<=n else 0 found=False for den in range(2*k-1,2*k+4): for deg in range(k,3*k+4): coeff,ok=fit(target,den,deg,N=35) if ok: poly=sum(coeff[d]*q**d for d in range(len(coeff))) print("k",k,"den",den,"deg",deg,"poly",s.factor(poly)) found=True raise SystemExit print("no",k)

Python

python
from math import comb def cat(n): return comb(2*n,n)//(n+1) def T(n,a,b): s=0 for i in range(n+1): if i>=a and n-i>=b: s += cat(i)*cat(n-i)*comb(i,a)*comb(n-i,b) return s for a,b in [(0,0),(1,0),(1,1),(2,0),(2,1),(2,2)]: vals=[T(n,a,b) for n in range(max(a+b,0),10)] print((a,b),vals)

Python

python
from math import comb def cat(n): return comb(2*n,n)//(n+1) N=30 # series q(z) qser=[0]*N for n in range(1,N): qser[n]=cat(n) def mul(a,b): c=[0]*N for i,x in enumerate(a): if not x: continue for j,y in enumerate(b): if i+j>=N: break c[i+j]+=x*y return c def add(a,b,coef=1): c=a[:] for i in range(min(N,len(b))): c[i]+=coef*b[i] return c def pow_series(base,e): res=[0]*N; res[0]=1 cur=base[:] while e: if e&1: res=mul(res,cur) cur=mul(cur,cur); e//=2 return res def inv1mq(exp): # (1-q)^(-exp) res=[0]*N; res[0]=1 term=[0]*N; term[0]=1 for m in range(1,N): term=mul(term,qser) coeff=comb(exp+m-1,m) for i in range(N): res[i]+=coeff*term[i] return res # fit S_k numerator degree maybe k+1 with denom (1-q)^(2k-1), times (1+q) from fractions import Fraction def fit_S(k,deg): target=[sum(comb(n,a) for a in range(k+1))*cat(n) for n in range(N)] den=inv1mq(max(1,2*k-1)) oneplusq=[0]*N; oneplusq[0]=1 for i in range(1,N): oneplusq[i]=qser[i] den=mul(den, oneplusq) basis=[]; cur=[0]*N; cur[0]=1 for d in range(deg+1): basis.append(mul(cur,den)) cur=mul(cur,qser) M=deg+1 AA=[[Fraction(basis[d][n]) for d in range(M)] for n in range(M)] bb=[Fraction(target[n]) for n in range(M)] r=0;piv=[] for c in range(M): pv=None for i in range(r,M): if AA[i][c]: pv=i;break if pv is None: continue AA[r],AA[pv]=AA[pv],AA[r]; bb[r],bb[pv]=bb[pv],bb[r] fac=AA[r][c] AA[r]=[x/fac for x in AA[r]]; bb[r]/=fac for i in range(M): if i!=r and AA[i][c]: fac=AA[i][c] AA[i]=[AA[i][j]-fac*AA[r][j] for j in range(M)] bb[i]=bb[i]-fac*bb[r] piv.append(c); r+=1 coeff=[Fraction(0)]*(deg+1) for i,c in enumerate(piv): coeff[c]=bb[i] # verify all out=[Fraction(0)]*N for d,c in enumerate(coeff): if c==0: continue for n in range(N): out[n]+=c*basis[d][n] return coeff, all(out[n]==target[n] for n in range(N)), out for k in range(1,8): for deg in range(0,8): coeff,ok,out=fit_S(k,deg) if ok: print("k",k,"deg",deg,coeff) break

Python

python
A,B=rec(12) for n in range(3,8): for k in range(1,n): # solve maybe A[n][k]-4A[n-1][k]+? print(n,k,A[n][k],4*A[n-1][k], A[n-1][k-1], A[n-1][k+1] if k+1<=n-1 else None) print()

Python

python
for n in range(2,8): row=[] for k in range(n+1): row.append(A[n][k]) print(n,row) print("normalize by 2^(n-k)") for n in range(2,8): print(n,[A[n][k]/(2**(n-k)) for k in range(n+1)])

Python

python
import sympy as s n,k=s.symbols('n k', integer=True, nonnegative=True) # try guess for fixed k small as combination of binomial(n+r, s)*4^n and Catalan-like terms? # let's use sympy sequence_guess maybe for k=2 seq=[A[i][2] for i in range(2,15)] print(seq)

Python

python
from math import comb def rec(nmax): B=[[0]*(nmax+1) for _ in range(nmax+1)] A=[[0]*(nmax+1) for _ in range(nmax+1)] B[0][0]=1 for n in range(1,nmax+1): for i in range(n): j=n-1-i for a in range(i+1): if not B[i][a]: continue for b in range(j+1): if not B[j][b]: continue val=B[i][a]*B[j][b] s=a+b B[n][s]+=val B[n][s+1]+=val t=max(a,b) A[n][t]+=val A[n][t+1]+=val return A,B A,B=rec(25) seq=[A[i][2] for

Python

python
from math import comb def rec(nmax): B=[[0]*(nmax+1) for _ in range(nmax+1)] A=[[0]*(nmax+1) for _ in range(nmax+1)] B[0][0]=1 for n in range(1,nmax+1): for i in range(n): j=n-1-i for a in range(i+1): if not B[i][a]: continue for b in range(j+1): if not B[j][b]: continue val=B[i][a]*B[j][b] s=a+b B[n][s]+=val B[n][s+1]+=val t=max(a,b) A[n][t]+=val A[n][t+1]+=val return A,B A,B=rec(35) N=45 def cat(n): return comb(2*n,n)//(n+1) qser=[0]*N for i in range(1,N): qser[i]=cat(i) def mul(a,b): c=[0]*N for i,x in enumerate(a): if not x: continue for j,y in enumerate(b): if i+j>=N: break c[i+j]+=x*y return c def inv_factor(exp1, exp2): # (1-q)^-exp1 * (1+q)^-exp2 # 1/(1+q)^exp2 = sum (-1)^m C(exp2+m-1,m) q^m # build q powers res1=[0]*N; res1[0]=1 term=[0]*N; term[0]=1 for m in range(1,N): term=mul(term,qser) coeff=comb(exp1+m-1,m) for i in range(N): res1[i]+=coeff*term[i] if exp2==0: return res1 res2=[0]*N; res2[0]=1 term=[0]*N; term[0]=1 for m in range(1,N): term=mul(term,qser) coeff=(-1)**m * comb(exp2+m-1,m) for i in range(N): res2[i]+=coeff*term[i] return mul(res1,res2) from fractions import Fraction def fit_target(target, exp1, exp2, maxdeg): den=inv_factor(exp1,exp2) basis=[]; cur=[0]*N; cur[0]=1 for d in range(maxdeg+1): basis.append(mul(cur,den)) cur=mul(cur,qser) M=maxdeg+1 AA=[[Fraction(basis[d][n]) for d in range(M)] for n in range(M)] bb=[Fraction(target[n]) for n in range(M)] r=0;piv=[] for c in range(M): pv=None for i in range(r,M): if AA[i][c]: pv=i;break if pv is None: continue AA[r],AA[pv]=AA[pv],AA[r]; bb[r],bb[pv]=bb[pv],bb[r] fac=AA[r][c] AA[r]=[x/fac for x in AA[r]]; bb[r]/=fac for i in range(M): if i!=r and AA[i][c]: fac=AA[i][c] AA[i]=[AA[i][j]-fac*AA[r][j] for j in range(M)] bb[i]=bb[i]-fac*bb[r] piv.append(c); r+=1 coeff=[Fraction(0)]*(maxdeg+1) for i,c in enumerate(piv): coeff[c]=bb[i] out=[Fraction(0)]*N for d,c in enumerate(coeff): if not c: continue for n in range(N): out[n]+=c*basis[d][n] return all(out[n]==target[n] for n in range(N)), coeff for k in range(1,6): target=[0]*N for n in range(1,N): if n<len(A) and k<=n: target[n]=A[n][k] found=False for exp2 in range(0,4): for exp1 in range(2*k-2,2*k+4): for deg in range(k,3*k+5): ok,coeff=fit_target(target,exp1,exp2,deg) if ok: print("k",k,"exp1",exp1,"exp2",exp2,"deg",deg,coeff) found=True; raise SystemExit print("none",k)

Python

python
from math import comb def cat(n): return comb(2*n,n)//(n+1) # q series N=15 q=[0]*N for i in range(1,N): q[i]=cat(i) # 1-q one_minus=[0]*N; one_minus[0]=1 for i in range(1,N): one_minus[i]=-q[i] # x series z/(1-4z) x=[0]*N for i in range(1,N): x[i]=4**(i-1) def mul(a,b): c=[0]*N for i,xv in enumerate(a): for j,yv in enumerate(b): if i+j>=N: break c[i+j]+=xv*yv return c x2=mul(x,one_minus) print(x2[1:10])

Python

python
# coeffs of x^2(1-q) def pow_series(base,e): res=[0]*N; res[0]=1 cur=base[:] while e: if e&1: res=mul(res,cur) cur=mul(cur,cur); e//=2 return res coef=mul(pow_series(x,2),one_minus) print(coef[2:10])

Python

python
import sympy as s z=s.symbols('z') expr=s.series((1-4*z)**(-s.Rational(3,2)),z,0,8).removeO() print([s.expand(expr).coeff(z,i) for i in range(8)]) expr2=s.series((1-4*z)**(-1),z,0,8).removeO() print([s.expand(expr2).coeff(z,i) for i in range(8)])

Python

python
from math import comb def cat(n): return comb(2*n,n)//(n+1) def formula(n,k): if k==0: return cat(n) ck=cat(k-1) ans=0 # square m=2*k-1 if m<=n: ans += ck*ck * (4**(n-m)) * comb(n-1,m-1) # same parity smaller j>=1 for j in range(k-2,0,-2): m=k+j-1 if m<=n: ans += 2*ck*cat(j-1) * (4**(n-m)) * comb(n-1,m-1) if k%2==0: r=n-k+1 if r>=0: half = comb(2*n,n-k+1) * 0 # placeholder # exact factorial formula half = comb(2*n, n) * comb(n, k-1) // comb(2*k-2, k-1) # maybe integer? # let's verify equivalence half2 = comb(2*n,n-k+1) * 0 special=(half - (4**(n-k+1))*comb(n-1,k-2))//2 ans += 2*ck*special return ans for n in range(1,12): for k in range(n+1): val=formula(n,k) if val!=A[n][k]: print("mismatch",n,k,val,A[n][k]) raise SystemExit print("all ok")

Python

python
MOD=10**9+7 def modpow(a,e): r=1 while e: if e&1:r=r*a%MOD a=a*a%MOD;e//=2 return r N=100 fact=[1]*(2*N+1) for i in range(1,2*N+1): fact[i]=fact[i-1]*i%MOD invfact=[1]*(2*N+1); invfact[-1]=pow(fact[-1],MOD-2,MOD) for i in range(2*N,0,-1): invfact[i-1]=invfact[i]*i%MOD pow4=[1]*(N+1) for i in range(1,N+1): pow4[i]=pow4[i-1]*4%MOD catm=[0]*(N+1) for i in range(N+1): catm[i]=fact[2*i]*invfact[i]%MOD*invfact[i]%MOD*pow(i+1,MOD-2,MOD)%MOD inv2=(MOD+1)//2 def C(n,r): if r<0 or r>n:return 0 return fact[n]*invfact[r]%MOD*invfact[n-r]%MOD def solve(n,k): if k==0:return catm[n] ck=catm[k-1] lim=min(k,n-k+1) start=1 if k%2 else 2 s=0 for j in range(start, lim+1, 2): m=k+j-1 s=(s + catm[j-1]*pow4[n-m]%MOD*C(n-1,m-1))%MOD ans=(2*ck*s - ck*ck%MOD*pow4[n-(2*k-1)]%MOD*C(n-1,2*k-2))%MOD if 2*k-1<=n else 2*ck*s%MOD if k%2==0: half = fact[2*n]*fact[k-1]%MOD*invfact[n]%MOD*invfact[2*k-2]%MOD*invfact[n-k+1]%MOD xprev = pow4[n-k+1]*C(n-1,k-2)%MOD special=(half - xprev)%MOD*inv2%MOD ans=(ans + 2*ck*special)%MOD return ans%MOD # compare A,B=rec(20) for n in range(1,15): for k in range(n+1): if solve(n,k)!=A[n][k]%MOD: print("bad",n,k,solve(n,k),A[n][k]); raise SystemExit print("all ok") print([solve(2,0),solve(2,1),solve(1,1),solve(7,6)])

思考

Clarifying path selection and root-to-node labeling

For root-to-node operations, we don't need to formalize everything immediately. Essentially, the set of root-to-node endpoints where y=1y = 1 reconstructs the labels. For cost distribution, using one-arm coefficients Cat_i binom(i,a) fits well, considering the number of used paths per vertex. The label for each vertex is determined by whether its root-to-vertex path is flipped.

Simplifying the operation cost analysis

For labelings of fixed shapes, we have exactly (na)\binom{n}{a} labelings, each with a one-arm cost of aa. For the full tree, we consider the counts of 1s in the subtrees, with operations based on toggling endpoints in left and right subtrees or on the root. The minimal cost formula is intuitive: pair 1s as much as possible across both sides, clear leftovers, and toggle the root if necessary. This gives a final cost of either max(a,b)\max(a, b) or max(a,b)+1\max(a, b) + 1.

Thought for 31m 5s

回答

Ниже — решение на C++ с выводом формулы за O(1) предусчёт + O(min(k, n-k+1)) на запрос.
Так как k <= n, а сумма всех n не больше 10^6, суммарная сложность O(10^6).

Идея

Для вершины v введём новую метку

bv=avaleft(v)aright(v),b_v = a_v \oplus a_{left(v)} \oplus a_{right(v)},

где у отсутствующего ребёнка метка считается 0.

Это преобразование биективно: по всем b_v исходные a_v восстанавливаются снизу вверх.

Теперь важно следующее:

  • если разрешены только пути вида root -> x, то операция меняет ровно один b_x;
  • значит, для фиксированной формы дерева число разметок с ровно t единицами среди b равно просто (nt)\binom{n}{t}.

Обозначим

Bn,t=Catn(nt),B_{n,t} = Cat_n \binom{n}{t},

где Cat_n — число форм бинарных деревьев из n вершин.


Как выглядит исходная операция в новых метках

Если выбрать путь через корень:

  • только в левое поддерево — меняется один b слева;
  • только в правое — один b справа;
  • корень-корень — только b_root;
  • левый конец + правый конец — меняются:
    • один b слева,
    • один b справа,
    • b_root.

Пусть в левом поддереве среди b ровно x единиц, в правом — y, а в корне b_root = c.

Тогда оптимально:

  • спарить min(x,y) единиц слева и справа,
  • остаток убрать одиночными путями,
  • в конце, возможно, убрать корень.

Стоимость равна

max(x,y)+(c(min(x,y)mod2)).\max(x,y) + (c \oplus ( \min(x,y)\bmod 2)).

Значит, для фиксированных x,y два возможных значения c дают стоимости:

  • max(x,y)
  • max(x,y)+1

ровно по одному разу.


Генерирующие функции

Пусть

C(z)=n0Catnzn=114z2z,C(z)=\sum_{n\ge 0} Cat_n z^n = \frac{1-\sqrt{1-4z}}{2z}, H(z,u)=n,t0Bn,tznut=C(z(1+u)).H(z,u)=\sum_{n,t\ge 0} B_{n,t} z^n u^t = C(z(1+u)).

После подстановки

q=C(z)1,x=q(1q)2=z14z,q = C(z)-1,\qquad x=\frac{q}{(1-q)^2}=\frac{z}{1-4z},

получается удобная формула, из которой после суммирования префиксов выводится

Fk(z)=n1An,kzn=2[k чётно]Catk1xk(1q)+2Catk1 ⁣ ⁣1jkjk(mod2) ⁣ ⁣Catj1xk+j1Catk12x2k1.F_k(z)=\sum_{n\ge 1} A_{n,k} z^n = 2[k\text{ чётно}]\,Cat_{k-1}\,x^k(1-q) + 2Cat_{k-1}\!\!\sum_{\substack{1\le j\le k\\ j\equiv k\pmod 2}}\!\!Cat_{j-1}x^{k+j-1} - Cat_{k-1}^2x^{2k-1}.

Остаётся взять коэффициенты.


Нужные коэффициенты

  1. Для m1m\ge 1:
[zn]xm=[zn](z14z)m=4nm(n1m1).[z^n]\,x^m = [z^n]\left(\frac{z}{1-4z}\right)^m = 4^{\,n-m}\binom{n-1}{m-1}.
  1. Для чётного k:
[zn]xk(1q)=12([znk+1](14z)(k12)[znk+1](14z)(k1)),[z^n]\,x^k(1-q) = \frac12\Big( [z^{n-k+1}](1-4z)^{-(k-\frac12)} - [z^{n-k+1}](1-4z)^{-(k-1)} \Big),

то есть

[zn]xk(1q)=12((2n)!(k1)!n!(2k2)!(nk+1)!4nk+1(n1k2)).[z^n]\,x^k(1-q) = \frac12\left( \frac{(2n)!(k-1)!}{n!(2k-2)!(n-k+1)!} - 4^{\,n-k+1}\binom{n-1}{k-2} \right).

Отсюда уже получается формула, которую можно считать за O(min(k,n-k+1)).


Код C++

cpp
#include <bits/stdc++.h> using namespace std; static const int MOD = 1000000007; long long mod_pow(long long a, long long e) { long long r = 1; while (e > 0) { if (e & 1) r = r * a % MOD; a = a * a % MOD; e >>= 1; } return r; } int main() { ios::sync_with_stdio(false); cin.tie(nullptr); int T; cin >> T; vector<pair<int,int>> qs(T); int maxN = 0; for (int i = 0; i < T; ++i) { int n, k; cin >> n >> k; qs[i] = {n, k}; maxN = max(maxN, n); } // factorials up to 2*maxN vector<long long> fact(2 * maxN + 1), invfact(2 * maxN + 1); fact[0] = 1; for (int i = 1; i <= 2 * maxN; ++i) fact[i] = fact[i - 1] * i % MOD; invfact[2 * maxN] = mod_pow(fact[2 * maxN], MOD - 2); for (int i = 2 * maxN; i >= 1; --i) invfact[i - 1] = invfact[i] * i % MOD; auto C = [&](int n, int r) -> long long { if (r < 0 || r > n) return 0; return fact[n] * invfact[r] % MOD * invfact[n - r] % MOD; }; // Catalan: Cat[n] = (2n)! / (n! (n+1)!) vector<long long> cat(maxN + 1); for (int n = 0; n <= maxN; ++n) { cat[n] = fact[2 * n] * invfact[n] % MOD * invfact[n + 1] % MOD; } vector<long long> pow4(maxN + 1, 1); for (int i = 1; i <= maxN; ++i) pow4[i] = pow4[i - 1] * 4 % MOD; const long long INV2 = (MOD + 1) / 2; auto coeff_x = [&](int n, int m) -> long long { // [z^n] (z/(1-4z))^m = 4^(n-m) * C(n-1, m-1) if (m < 1 || m > n) return 0; return pow4[n - m] * C(n - 1, m - 1) % MOD; }; auto coeff_special = [&](int n, int k) -> long long { // [z^n] x^k (1-q), only used for even k // = 1/2 * ( (2n)! (k-1)! / (n! (2k-2)! (n-k+1)!) // - 4^(n-k+1) * C(n-1, k-2) ) long long first = fact[2 * n]; first = first * fact[k - 1] % MOD; first = first * invfact[n] % MOD; first = first * invfact[2 * k - 2] % MOD; first = first * invfact[n - k + 1] % MOD; long long second = pow4[n - k + 1] * C(n - 1, k - 2) % MOD; long long res = (first - second) % MOD; if (res < 0) res += MOD; res = res * INV2 % MOD; return res; }; for (auto [n, k] : qs) { if (k == 0) { cout << cat[n] << '\n'; continue; } long long ck = cat[k - 1]; long long sum = 0; int start = (k & 1) ? 1 : 2; int lim = min(k, n - k + 1); for (int j = start; j <= lim; j += 2) { int m = k + j - 1; long long add = cat[j - 1] * coeff_x(n, m) % MOD; sum += add; if (sum >= MOD) sum -= MOD; } long long ans = 2LL * ck % MOD * sum % MOD; // j = k was counted with coefficient 2, but must be 1 if (2 * k - 1 <= n) { long long sq = ck * ck % MOD * coeff_x(n, 2 * k - 1) % MOD; ans -= sq; if (ans < 0) ans += MOD; } if ((k & 1) == 0) { long long sp = coeff_special(n, k); ans += 2LL * ck % MOD * sp % MOD; if (ans >= MOD) ans -= MOD; } cout << ans % MOD << '\n'; } return 0; }

Сложность

Предобработка: O(maxN)
Один запрос: O(min(k, n-k+1))
Суммарно по всем тестам: O(sum n) ≤ O(10^6)

Если хочешь, я могу ещё отдельно расписать короткое “редакционное” доказательство формулы для F_k(z) без пропусков.

分享這個問答