来一个不用 trie,但是需要大力分讨的做法。
这个 min 的限制是很难受的:因为如果两个数在某一位上分别是 \(0\) 和 \(1\),那个 \(1\) 仍然可能有贡献。
先考虑一下性质 A: \(a_i\) 全相等的做法。也即求 \(n^2\sum_{j=0}^{m}x\oplus j\)。这个东西就能拆位了,考虑第 \(i\) 位什么时候有贡献,当 \(x\) 的这一位和 \(j\) 的这一位不同时,有 \(2^j\) 的贡献。那么我们实际上把问题转化成了一个计数问题:求 \(0\sim m\) 中钦定第 \(i\) 位为 \(0\)(或是 \(1\))的数的个数。这个东西是很好做的,但是需要一些分讨。可以看代码。
然后我们把 min 转化成 max。方法是先求出 \(\sum_i\sum_j\sum_ka_i\oplus k+a_j\oplus k\)。 这个东西 \(i,j\) 无关,是很好做的。然后这个东西减去 max 就是 min(不转化好像也能做,但是我场上把题看错了,只好这样将错就错。)
重新观察一下式子:要求和的项是两个数异或上同一个数,这个东西有一个很好的性质:若 \(x,y\) 的第 \(i\) 位不同,那么它们异或上同一个数后仍不同。
这个性质是很强的!对于两个不相等的数,我们可以在它们最高的不相同的位处确定大小关系,进而很方便的算贡献:仍然是拆位,然后问题被转化成了上面的计数问题的加强版:限制的位置变成了两个,这个东西仍然可以大力分讨完成,当然也可以数位 DP。
最后,怎么快速进行这个过程呢?用类似基数排序的方法:我们要对一些最高的 \(x\) 位都相同的数按第 \(x\) 位是什么给它们分组。然后组和组间的贡献就可以用上面的方法算。组内贡献只需要递归到最后一层,这时组内所有数都相等,然后用性质 A 的方法去做。
这样这个题就做完了,复杂度为 \(O(n\log V)\)。
我的代码可能写的有些冗长,应该有更简单的写法。如果把那几个计数的函数换成数位 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 位也是 0int 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";}