luogu P11799 题解
来一个不用 trie,但是需要大力分讨的做法。
这个 min 的限制是很难受的:因为如果两个数在某一位上分别是
先考虑一下性质 A:
然后我们把 min 转化成 max。方法是先求出
重新观察一下式子:要求和的项是两个数异或上同一个数,这个东西有一个很好的性质:若
这个性质是很强的!对于两个不相等的数,我们可以在它们最高的不相同的位处确定大小关系,进而很方便的算贡献:仍然是拆位,然后问题被转化成了上面的计数问题的加强版:限制的位置变成了两个,这个东西仍然可以大力分讨完成,当然也可以数位 DP。
最后,怎么快速进行这个过程呢?用类似基数排序的方法:我们要对一些最高的
这样这个题就做完了,复杂度为
我的代码可能写的有些冗长,应该有更简单的写法。如果把那几个计数的函数换成数位 DP 应该能短不少。
int calc0(int m, int j) { // 计算 [0, m] 中第 j 位是 0 的数的个数,下同 int tot = (1 << j) * (m >> j + 1); if (m & (1 << j)) { tot += 1 << j; } else { tot += (m & ((1 << j) - 1)) + 1; } return tot; } int calc1(int m, int j) { int tot = (1 << j) * (m >> j + 1); tot += max(0, 1 + (m & ((1 << j + 1) - 1)) - (1 << j)); return tot; } int calc00(int m, int x, int y) { // 第 x 位是 0,第 y 位也是 0 int cnt = 0; cnt += (m >> x + 1) * (1 << x - 1); if (m & (1 << x)) { cnt += 1 << (x - 1); } else cnt += calc0(m & ((1 << x) - 1), y); return cnt; } int calc01(int m, int x, int y) { int cnt = 0; cnt += (m >> x + 1) * (1 << x - 1); if (m & (1 << x)) { cnt += 1 << x - 1; } else { cnt += calc1(m & ((1 << x) - 1), y); } return cnt; } int calc10(int m, int x, int y) { int cnt = 0; cnt += (m >> x + 1) * (1 << x - 1); if (m & (1 << x)) { int t = m & ((1 << x) - 1); cnt += calc0(t, y); } return cnt; } int calc11(int m, int x, int y) { int cnt = 0; cnt += (m >> x + 1) * (1 << x - 1); if (m & (1 << x)) { int t = m & ((1 << x) - 1); cnt += calc1(t, y); } return cnt; } void solve() { int n, m; cin >> n >> m; vi a(n + 1); F (i, 1, n) cin >> a[i]; i64 ans = 0; auto solve = [&](auto&& solve, int bit, int l, int r) -> void { if (bit == -1) { int x = a[l], cnt = 1LL * (r - l + 1) * (r - l + 1) % mod; DF (j, w - 1, 0) { if (x & (1 << j)) { ans += 1LL * (1 << j) * calc0(m, j) % mod * cnt % mod; ans %= mod; } else { ans += 1LL * (1 << j) * calc1(m, j) % mod * cnt % mod; ans %= mod; } } return; } vi h; h.reserve(r - l + 1); int st = 0, ed = r - l; F (i, l, r) { if (!(a[i] & (1 << bit))) { h[ed--] = a[i]; } else h[st++] = a[i]; } F (i, l, r) a[i] = h[i - l]; // 这里 [l, l + st - 1] 这个区间中的数第 bit 位是 1,其余的数这一位是 0. if ((st and l + st <= r)) { // 这里有一个小剪枝,删了也不影响复杂度。 { i64 res = 0; F (i, l, l + st - 1) { DF (j, w - 1, bit + 1) { if (a[i] & (1 << j)) { res += 1LL * (1 << j) * calc00(m, j, bit) % mod; } else { res += 1LL * (1 << j) * calc10(m, j, bit) % mod; } } res += 1LL * (1 << bit) * calc0(m, bit) % mod; DF (j, bit - 1, 0) { if (a[i] & (1 << j)) { res += 1LL * (1 << j) * calc00(m, bit, j) % mod; } else { res += 1LL * (1 << j) * calc01(m, bit, j) % mod; } } res %= mod; } ans += 2 * res * (r - (l + st) + 1) % mod; ans %= mod; } { i64 res = 0; F (i, l + st, r) { DF (j, w - 1, bit + 1) { if (a[i] & (1 << j)) { res += 1LL * (1 << j) * calc01(m, j, bit) % mod; } else { res += 1LL * (1 << j) * calc11(m, j, bit) % mod; } } res += 1LL * (1 << bit) * calc1(m, bit) % mod; DF (j, bit - 1, 0) { if (a[i] & (1 << j)) { res += 1LL * (1 << j) * calc10(m, bit, j) % mod; } else { res += 1LL * (1 << j) * calc11(m, bit, j) % mod; } } res %= mod; } ans += 2 * res * st % mod; ans %= mod; } } if (l <= l + st - 1) solve(solve, bit - 1, l, l + st - 1); if (l + st <= r) solve(solve, bit - 1, l + st, r); }; solve(solve, w - 1, 1, n); // 进行完这个过程,a 是降序的。 i64 ALL = 0; F (i, 1, n) { // 这里就是利用 a + b - max(a, b) = min(a, b) int x = a[i]; i64 res = 0; DF (j, w - 1, 0) { if (x & (1 << j)) { res += 1LL * (1 << j) * calc0(m, j); res %= mod; } else { res += 1LL * (1 << j) * calc1(m, j); res %= mod; } } res = res * 2 * n % mod; ALL = (ALL + res) % mod; } ALL -= ans; ALL %= mod; if (ALL < 0) ALL += mod; cout << ALL << "\n"; }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现