题解: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}\)。完整的转移就是

\[f_i=f_{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}\),仿照前面的转移容易得到

\[g_i=g_{i-1}+g_{i-1}^2-\dbinom{g_{i-1}}{2} \]

于是可以 \(\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\) 个可用的叶子。仿照前面的转移容易得出答案为

\[f_{\max(c_L,c_R)}+f_{c_L}f_{c_R}-\dbinom{f_{\min(c_L,c_R)}}{2} \]

时间复杂度为 \(\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}\)。于是可以得到转移

\[g_i=\sum_{k=1}^B\dbinom{g_{i-1}+k-1}{k}=\dbinom{g_{i-1}+B}{B}-1 \]

对于 \(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}\),因此这部分的贡献为

\[\dbinom{g_k+c+1}{c+2}-\dbinom{g_k-f_{\min(c_L,c_R)}+c+1}{c+2}-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;
}
posted @ 2026-08-30 23:48  P2441M  阅读(10)  评论(0)    收藏  举报