题解:P17339 【MX-X30-T5】メタモリボン
很有意思。
\(B=2\),\(L=0\)
令 \(f_i\) 表示只允许使用 \(0\sim i-1\) 这些叶子时的答案,\(F_i\) 表示可能的树构成的集合。设 \(k=\lfloor\log_2(i)\rfloor\),先假设 \(2^k<i<2^{k+1}\),那么 \(0\sim i-1\) 会分成左子树中完整的 \(2^k\) 个叶子和右子树中的 \(i-2^k\) 个叶子。若最终根节点度数为 \(1\),那么对应的树构成的集合为 \(F_{2^k}\cup F_{i-2^k}=F_{2^k}\),贡献为 \(f_{2^k}\);若最终根节点度数为 \(2\),不妨先计算 \(f_{2^k}f_{i-2^k}\),这里 \((T_x,T_y)\) 会被算重当且仅当 \(T_x,T_y\in F_{2^k}\cap F_{i-2^k}=F_{i-2^k}\),因此实际贡献为 \(f_{2^k}f_{i-2^k}-\dbinom{f_{i-2^k}}{2}\)。完整的转移就是
还需要处理 \(i=2^k\) 的情况。设 \(g_i=f_{2^i}\),\(G_i=F_{2^i}\),仿照前面的转移容易得到
于是可以 \(\mathcal{O}(\log R)\) 处理出 \(g\)。计算 \(f_{R+1}\) 时只会额外计算计算 \(\mathcal{O}(\log{R})\) 个 \(f\) 的值。
\(B=2\)
考虑 \(L>0\) 咋做。取 \(k=\lfloor\log_2(L\oplus R)\rfloor\) 为 \(L,R\) 最高的不同位,显然不用考虑比 \(k\) 更高的位,于是我们以分叉点为根节点即可。此时左子树中有 \(c_L=2^k-(L\bmod{2^k})\) 个可用的叶子,右子树中有 \(c_R=(R\bmod{2^k})+1\) 个可用的叶子。仿照前面的转移容易得出答案为
时间复杂度为 \(\mathcal{O}(T\log{R})\)。
\(\mathcal{O}(TB\log_B{R})\) 做法
先计算 \(g_i=f_{B^i}\),那么根节点有 \(B\) 个儿子,每个儿子都可以取 \(G_{i-1}\) 中的一个树作为子树结构。考虑枚举根节点度数 \(k\),那么变成求有多少大小为 \(k\) 的多重集,集合中每个元素都是 \([1,g_{i-1}]\) 中的正整数。这是经典插板,方案数为 \(\dbinom{g_{i-1}+k-1}{k}\)。于是可以得到转移
对于 \(f_i\),设 \(k=\lfloor\log_B(i)\rfloor\),\(i=qB^k+r\),则前 \(q\) 个子树中有完整的 \(B^k\) 个叶子,第 \(q+1\) 个子树内有 \(r\) 个叶子。若 \(r=0\),则直接有 \(f_i=\dbinom{g_k+q}{q}-1\);若 \(r>0\),则相当于要求多重集中存在至少一个数 \(\leq f_r\),我们正难则反,容易得到 \(f_i=\dbinom{g_k+q+1}{q+1}-1-\dbinom{g_k-f_r+q}{q+1}\)。
计算答案时,还是取 \(L,R\) 最高的不同位 \(k\)。设 \(L,R\) 在 \(B\) 进制表示下的第 \(k\) 位分别为 \(d_L,d_R\),那么中间的 \(c=d_R-d_L-1\) 个子树内有完整的 \(B^k\) 个叶子,左侧 \(L\) 所在的子树内有 \(c_L=B^k-(L\bmod{2^k})\) 个叶子,右侧 \(R\) 所在的子树内有 \(c_R=(R\bmod{B^k})+1\) 个叶子。
若根节点度数 \(\leq c\),则贡献为 \(\dbinom{g_k+c}{c}-1\)。
若根节点度数为 \(c+1\),则多重集中存在至少一个数 \(\leq f_{\max(c_L,c_R)}\),正难则反得出贡献为 \(\dbinom{g_k+c}{c+1}-\dbinom{g_k-f_{\max(c_L,c_R)}+c}{c+1}\)。
若根节点度数为 \(c+2\),则多重集中存在至少一个数 \(\leq f_{\min(c_L,c_R)}\),存在至少两个数 \(\leq f_{\max(c_L,c_R)}\)。手动容斥一下,若只满足第一条限制,则方案数为 \(\dbinom{g_k+c+1}{c+2}-\dbinom{g_k-f_{\min(c_L,c_R)}+c+1}{c+2}\)。再减去只有一个数 \(\leq f_{\min(c_L,c_R)}\),其他数都 \(>f_{\max(c_L,c_R)}\) 的方案数,也就是 \(f_{\min(c_L,c_R)}\dbinom{g_k-f_{\max(c_L,c_R)}+c}{c+1}\),因此这部分的贡献为
显然我们用到的组合数的下指标都 \(\leq B\)。如果暴力计算,时间复杂度为 \(\mathcal{O}(TB\log_B{R})\)。
但是 \(B\) 太大了我们就飞了,那咋办?
\(\mathcal{O}(T\sqrt{R}\log_B{R})\) 做法
从 \(g\) 入手,事实上 \(g_1=\dbinom{B+1}{B}-1=B\);而如果要用到 \(i\geq 2\) 的 \(g_i\),此时必然有 \(B\leq\sqrt{R}\),暴力算组合数勉强可以承受。
对于 \(f\),若 \(i<B\),即 \(k=0\),则 \(f_i=\dbinom{g_0+i}{i}-1=i\);否则有 \(q\leq\min(B,\dfrac{R}{B})\leq\sqrt{R}\),可以暴力计算。
计算答案时,若 \(k=0\),则答案为 \(R-L+1\),否则还是暴力计算。
这样时间复杂度为 \(\mathcal{O}(T\sqrt{R}\log_B{R})\),可以获得 \(85\) 分。
\(\mathcal{O}(T\sqrt[3]{R}\log_B{R})\) 做法
尝试继续往立方根上面压。
还是从 \(g\) 入手,我们发现 \(g_2=\dbinom{2B}{B}-1\),到这里必然有 \(B\leq\sqrt{R}\),所以实际上可以提前预处理 \(\mathcal{O}(1)\) 计算;而如果要用到 \(i\geq 3\) 的 \(g_i\) 就会有 \(B\leq\sqrt[3]{R}\)。
对于 \(f\),若 \(B\leq i<B^2\) 且 \(r>0\),则 \(f_i=\dbinom{B+q+1}{q+1}-1-\dbinom{B-f_r+q}{q+1}\)。观察到 \(r<B\),因此 \(f_r=r\),所以实际上这里组合数的上下标都不算很大。设阈值 \(K=10^6\),我们把阶乘和阶乘逆元预处理到 \(2K\) 范围,若 \(B\leq K\) 则 \(\mathcal{O}(1)\) 计算,否则 \(q\leq \dfrac{R}{K}\leq 10^3\),依然可以暴力计算。对于 \(i\geq B^2\),显然 \(q\leq \min(B,\dfrac{R}{B^2})\leq\sqrt[3]{R}\)。
计算答案时,若 \(k=1\),则 \(c_L,c_R\leq B\),\(g_1=B\),因此用和前面一样的方法处理即可;若 \(k\geq 2\),则 \(c\leq \min(B,\dfrac{R}{B^2})\leq\sqrt[3]{R}\)。
这样我们就做到了 \(\mathcal{O}(T\sqrt[3]{R}\log_B{R})\),可以通过本题。
主要代码
int tc, B, L, R;
ll pw[31];
mint fac[MAXN], ifac[MAXN], g[31];
void init(int n) {
fac[0] = 1;
for (int i = 1; i <= n; ++i) fac[i] = fac[i - 1] * i;
ifac[n] = fac[n].inv();
for (int i = n - 1; i >= 0; --i) ifac[i] = ifac[i + 1] * (i + 1);
}
mint facC(int n, int m) {
return n < 0 || m < 0 || n < m ? 0 : fac[n] * ifac[m] * ifac[n - m];
}
mint C(mint n, int m) {
mint res = ifac[m];
for (int i = 0; i < m; ++i) res *= n - i;
return res;
}
mint solve(int x) {
int t = 0;
while (pw[t + 1] <= x) ++t;
if (pw[t] == x) return g[t];
if (!t) {
return x;
} else {
auto comb = [&](mint n, int m) {
return t == 1 && B <= 1000000 ? facC(n.val(), m) : C(n, m);
};
int q = x / pw[t], r = x % pw[t];
if (!r) {
return comb(g[t] + q, q) - 1;
} else {
mint p = solve(r);
return comb(g[t] + q + 1, q + 1) - 1 - comb(g[t] - p + q, q + 1);
}
}
}
int main() {
ios::sync_with_stdio(false);
cin.tie(nullptr);
init(MAXN - 5);
cin >> tc;
while (tc--) {
cin >> B >> L >> R;
if (L == R) {
cout << "1\n";
continue;
}
pw[0] = 1;
int t = 0;
for (int i = 1;; ++i) {
pw[i] = pw[i - 1] * B;
if (pw[i] <= R) t = i;
else break;
}
g[0] = 1;
for (int i = 1; i <= t; ++i) {
if (i == 1) g[i] = B;
else if (i == 2) g[i] = facC(B * 2, B) - 1;
else g[i] = C(g[i - 1] + B, B) - 1;
}
int k = -1;
for (int i = t; i >= 0; --i) {
if ((L / pw[i]) % B != (R / pw[i]) % B) {
k = i;
break;
}
}
if (!k) {
cout << R - L + 1 << '\n';
continue;
}
auto comb = [&](mint n, int m) {
return k == 1 && B <= 1000000 ? facC(n.val(), m) : C(n, m);
};
int dl = (L / pw[k]) % B, dr = (R / pw[k]) % B;
int cl = pw[k] - L % pw[k], cr = R % pw[k] + 1;
if (cl < cr) swap(cl, cr);
mint p = solve(cl), q = solve(cr);
int cnt = dr - dl - 1;
mint ans = comb(g[k] + cnt, cnt) - 1;
ans += comb(g[k] + cnt, cnt + 1) - comb(g[k] - p + cnt, cnt + 1);
ans += comb(g[k] + cnt + 1, cnt + 2) - comb(g[k] - q + cnt + 1, cnt + 2) - q * comb(g[k] - p + cnt, cnt + 1);
cout << ans << '\n';
}
return 0;
}

浙公网安备 33010602011771号