luogu P11799 题解

来一个不用 trie,但是需要大力分讨的做法。

这个 min 的限制是很难受的:因为如果两个数在某一位上分别是 01,那个 1 仍然可能有贡献。

先考虑一下性质 A: ai 全相等的做法。也即求 n2j=0mxj。这个东西就能拆位了,考虑第 i 位什么时候有贡献,当 x 的这一位和 j 的这一位不同时,有 2j 的贡献。那么我们实际上把问题转化成了一个计数问题:求 0m 中钦定第 i 位为 0(或是 1)的数的个数。这个东西是很好做的,但是需要一些分讨。可以看代码。

然后我们把 min 转化成 max。方法是先求出 ijkaik+ajk。 这个东西 i,j 无关,是很好做的。然后这个东西减去 max 就是 min(不转化好像也能做,但是我场上把题看错了,只好这样将错就错。)

重新观察一下式子:要求和的项是两个数异或上同一个数,这个东西有一个很好的性质:若 x,y 的第 i 位不同,那么它们异或上同一个数后仍不同。

这个性质是很强的!对于两个不相等的数,我们可以在它们最高的不相同的位处确定大小关系,进而很方便的算贡献:仍然是拆位,然后问题被转化成了上面的计数问题的加强版:限制的位置变成了两个,这个东西仍然可以大力分讨完成,当然也可以数位 DP。

最后,怎么快速进行这个过程呢?用类似基数排序的方法:我们要对一些最高的 x 位都相同的数按第 x 位是什么给它们分组。然后组和组间的贡献就可以用上面的方法算。组内贡献只需要递归到最后一层,这时组内所有数都相等,然后用性质 A 的方法去做。

这样这个题就做完了,复杂度为 O(nlogV)

我的代码可能写的有些冗长,应该有更简单的写法。如果把那几个计数的函数换成数位 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";
}
posted @   rhineofts  阅读(5)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示