洛谷 7976 「Stoi2033」园游会 题解

思路

先证明一下打表找出的规律.

引理:记 ii 的三进制表达中 11 的个数为 ct\mathrm{ct},有

j((ij)+1)mod31=2ct\sum_{j} \left(\binom{i}{j} + 1\right) \bmod 3 - 1 = 2^\mathrm{ct}

在我校 MO 爷的帮助下给出的证明:

设满足 (nm)1(mod3)\binom{n}{m} \equiv 1 \pmod 3mmaa 个,满足 (nm)2(mod3)\binom{n}{m} \equiv 2 \pmod 3mmbb 个,即证明 ab=2cta - b = 2^\mathrm{ct}

考虑对 (nm)mod3\binom{n}{m} \bmod 3 应用 Lucas 定理,设 n,mn, m 的三进制表达分别为 (n1n2np)3,(m1m2mp)3(n_1 n_2 \cdots n_p)_3, (m_1 m_2 \cdots m_p)_3,有 (nm)i(nimi)(mod3)\binom{n}{m} \equiv \prod_i \binom{n_i}{m_i} \pmod 3.记 r,s,tr, s, t 分别为 nin_i 等于 0,1,20, 1, 2ii 构成的集合.

考虑计算 aa.要使 (nm)1(mod3)\binom{n}{m} \equiv 1 \pmod 3mm 需要满足下列条件:

  • 由于 r,sr, s 中的位对乘积的贡献只能为 11,那么有 ir,mi=0\forall i \in r, m_i = 0is,mi{0,1}\forall i \in s, m_i \in \{0, 1\}
  • 由于 2k1(mod3)    2k2^k \equiv 1 \pmod 3 \iff 2 \mid k,有 it[mi=1]\sum\limits_{i \in t} [m_i = 1] 是偶数.

枚举有多少 iti \in t 使得 mi=1m_i = 1,有

a=2sk=0t[2k](tk)2tka = 2^{|s|} \sum_{k = 0}^{|t|} [2 \mid k] \binom{|t|}{k} 2^{|t| - k}

同理可得

b=2sk=0t[2k](tk)2tkb = 2^{|s|} \sum_{k = 0}^{|t|} [2 \nmid k] \binom{|t|}{k} 2^{|t| - k}

那么有

ab=2sk=0t(tk)(1)k2tka - b = 2^{|s|} \sum_{k = 0}^{|t|} \binom{|t|}{k} (-1)^k 2^{|t| - k}

这不是我们二项式定理的题目吗!于是有

ab=2s(21)t=2s=2cta - b = 2^{|s|} (2 - 1)^{|t|} = 2^{|s|} = 2^\mathrm{ct}

得证.

然后就可以枚举 ct\mathrm{ct},数位 DP 计算 n\le n 的数中有几个满足三进制表达中 11 的个数为 ct\mathrm{ct}

代码

卡了下常,写得很丑.

#include <cstdio>
#include <cstring>
#include <algorithm>
#define debug(...) fprintf(stderr, __VA_ARGS__)

using u64 = unsigned long long;

inline u64 rd() {
	u64 x = 0; int c = getchar();
	while (((c - '0') | ('9' - c)) < 0) c = getchar();
	while (((c - '0') | ('9' - c)) > 0)
		x = x * 10 + c - '0', c = getchar();
	return x;
}

const u64 P = 1732073999;

const int N = 40;

int nn[N + 5], len;
u64 pw[N + 5];

u64 f[N + 5][N + 5][2];
u64 dfs(int i, int ct, bool rmx) {
	if (i == len + 1) return ct == 0;
	if (f[i][ct][rmx] != 0xffffffffffffffff) return f[i][ct][rmx];
	u64 res = 0;
	for (int cur = 0, li = (rmx ? nn[i] : 2); cur <= li; cur++) {
		int nc = ct - (cur == 1);
		if (nc < 0) continue;
		int nxt = dfs(i + 1, nc, rmx && cur == nn[i]);
		res += nxt;
	}
	return f[i][ct][rmx] = res % P;
}

void solve() {
	memset(f, 0xff, sizeof(f));

	u64 n = rd(); len = 0;
	while (n) nn[++len] = n % 3, n /= 3;
	std::reverse(nn + 1, nn + 1 + len);
	u64 ans = 0;
	for (int i = 0; i <= len; i++) {
		ans += dfs(1, i, 1) * pw[i] % P;
	}
	printf("%llu\n", ans % P);
}

int main() {
	pw[0] = 1; for (int i = 1; i <= N; i++) pw[i] = pw[i - 1] * 2 % P;
	int T = rd(); rd();
	while (T--) solve();
	return 0;
}