一个比较清真的计数DP,因为一个细节想了挺久。
我们发现可以把跳的过程看做一棵树,
B
B
B的父亲为
A
A
A,我们需要不断把儿子合并到父亲,每次把
B
B
B合并到
A
A
A后坐标会变成
2
B
−
A
2B-A
2B−A。那么考虑每个点对最终答案的贡献系数,是
2
深
度
∗
(
−
1
)
儿
子
数
目
+
从
父
亲
到
根
被
合
并
时
是
第
几
个
的
和
−
深
度
2^{深度}*(-1)^{儿子数目+从父亲到根被合并时是第几个的和-深度}
2深度∗(−1)儿子数目+从父亲到根被合并时是第几个的和−深度。因为进制非常大,所以两个方案最终落到的位置不同当且仅当两个点的贡献系数不同。
考虑每一层的点数,每一层中贡献系数是
(
−
1
)
奇
(-1)^奇
(−1)奇的位置数目,那么就可以用组合数算出最终的方案数。但是那个
(
−
1
)
k
(-1)^k
(−1)k贡献非常奇怪,我们先忽略掉深度,同时考虑儿子数目和从父亲到根被合并时是第几个的和不好做,不过我们发现其实可以不用管儿子数目的奇偶。如果我们已经知道了每一层的从父亲到根被合并时是第几个的和为奇数的点数和每层的点数,我们可以还原出每一层的实际贡献(假设第
i
i
i层从父亲到根被合并时是第几个的和为奇数的点数是
k
k
k,第
i
+
1
i+1
i+1层奇与偶的差为
d
≥
0
d\geq 0
d≥0,那么需要满足
k
≥
d
k \geq d
k≥d,并且第
i
i
i层真实贡献为奇数的点数目是
k
−
d
k-d
k−d)。
于是我们随便DP一下就好了,我的实现是
O
(
n
5
)
\mathcal O(n^5)
O(n5)的,不过可以优化到
O
(
n
4
)
\mathcal O(n^4)
O(n4)甚至
O
(
n
3
)
\mathcal O(n^3)
O(n3)。
#include <bits/stdc++.h>
#define MOD 1000000007
using namespace std;
typedef long long ll;
ll pow_mod(ll x,int k) {
ll ans=1;
while (k) {
if (k&1) ans=ans*x%MOD;
x=x*x%MOD;
k>>=1;
}
return ans;
}
inline void add(int &x,ll y) {
x=(x+y)%MOD;
}
ll facd[55],facv[55];
void pre(int n) {
facd[0]=1;
for(int i=1;i<=n;i++) facd[i]=facd[i-1]*i%MOD;
facv[n]=pow_mod(facd[n],MOD-2);
for(int i=n-1;i>=0;i--) facv[i]=facv[i+1]*(i+1)%MOD;
}
int f[55][55][55];
int dp(int n) {
f[1][1][0]=1;
for(int i=1;i<n;i++)
for(int j=0;j<=i;j++)
for(int k=0;j+k<=i;k++)
if (f[i][j][k]) {
for(int t1=0;i+t1<=n;t1++)
for(int t2=0;i+t1+t2<=n;t2++)
if (t1||t2) {
if (t1>t2) {
if (t1-t2>j) continue;
add(f[i+t1+t2][t1][t2],f[i][j][k]*facv[j-(t1-t2)]%MOD*facv[k+(t1-t2)]);
}
else {
if (t2-t1>k) continue;
add(f[i+t1+t2][t1][t2],f[i][j][k]*facv[j+(t2-t1)]%MOD*facv[k-(t2-t1)]);
}
}
}
int ans=0;
for(int i=0;i<=n;i++)
for(int j=0;i+j<=n;j++)
if (f[n][i][j]) add(ans,f[n][i][j]*facv[i]%MOD*facv[j]);
return ans*facd[n]%MOD;
}
int main() {
int n;
scanf("%d",&n);
pre(n);
printf("%d\n",dp(n));
return 0;
}