[题解]P17293 [Algo Beat Contest 013 & MSOI R2] 好朋友
对于一个三元组 \((i,j,k)\) 的贡献,本质上还是两个元素两两之间的关系,单独考虑每一个 \(\operatorname{popcount}\)。对于 \(a_i,a_j\) 这两个元素来说,它们对总价值的贡献是 \(6n \times \operatorname{popcount}(a_i,a_j)\),其中构成一个三元组需要再任取一个元素,一个本质相同的三元组可以交换成 \(6\) 个不同的三元组。那么若 \(k \bmod 6n \neq 0\),则一定无解。
价值和位运算有关,考虑逐位分析每一位上的贡献。假设当前正在考虑第 \(i\) 位的贡献,只有第 \(i\) 位是 1 的元素选择了一个是 0 的元素,它们两个异或起来才是 1,才能对价值和产生贡献。令 \(num_{i,0/1}\) 表示 \(a\) 中第 \(i\) 位上为 0/1 的数量,则满足条件的 \(a\) 必须满足 \(6n\sum_{i = 0} num_{i,0} \times num_{i,1} = k\)。注意到反转第 \(i\) 位上的 0/1 对价值和没有任何影响,且有 \(a_i \leq m\) 的限制,我们当然希望 1 的数量越少越好,不妨钦定 \(num_{i,0} \leq \lfloor \frac{n}{2} \rfloor\)。
令 \(B = \lfloor \log m \rfloor + 1\),若存在位数超过 \(B\) 的 1,则这个 \(a_i\) 必定超过 \(m\),所以无需考虑位数超过 \(B\) 的情况。若暂时忽略掉 \(a_i \leq m\) 的限制,那么问题转变为了,能否构造出一个序列 \(num_i \in [0,\lfloor \frac{n}{2} \rfloor]\),使得 \(\sum_{i = 0}^B num_i(n - num_i) = \frac{k}{6n}\)。等式右边是个常数,不妨记 \(C = \frac{k}{6n}\)。
考虑一个 dp,定义 \(dp_{i,j}\) 表示考虑前 \(i\) 位,能否使 \(\sum_{p = 0}^{i - 1}num_p(n - num_p) = j\):
这个 dp 形式明显是可以用 bitset 优化的,复杂度 \(\Theta(\frac{nBC}{w})\)。感性理解一下,发现当 \(C\) 太大的时候根本不可能有解,尝试寻找一下 \(C\) 的上界,为:\(B \lceil \frac{n^2}{4} \rceil\),当 \(num_i = \lfloor \frac{n}{2} \rfloor\) 时取得。也就是说当 \(C > B \lceil \frac{n^2}{4} \rceil\) 的时候无解。你猜一下这个时候把这个上界卡住就能过了,于是你打表发现 \(C \leq 6 \times 10^5\),\(\Theta(\frac{nBC}{w}) \approx 5 \times 10^7\),完全可以过。
接下来考虑构造,从高到低贪心,尽可能少用 1,可以通过 \(dp\) 知道这一位最少能用几个 1。不妨记 \(cnt_i\) 表示第 \(i\) 位最少可以用几个 1,依旧从高到低钦定,每一位选择目前最小的元素加上去,最后如果算出来的 \(a\),\(\exist a_i > m\) 就是无解。
#include <bits/stdc++.h>
#define re register
#define int long long
using namespace std;
const int N = 1e6 + 10,M = 6e5 + 10;
int n,m,k,B;
bitset<M> dp[24];
int cnt[24],ans[N];
inline int read(){
int r = 0,w = 1;
char c = getchar();
while (c < '0' || c > '9'){
if (c == '-') w = -1;
c = getchar();
}
while (c >= '0' && c <= '9'){
r = (r << 3) + (r << 1) + (c ^ 48);
c = getchar();
}
return r * w;
}
inline int getlog(int x){
int res = 0;
while (x) res++,x >>= 1;
return res;
}
signed main(){
n = read(),m = read(),k = read();
if (k % (n * 6)) return puts("-1"),0;
k /= (n * 6),B = getlog(m) + 1;
if (k > B * (n / 2) * (n - n / 2)) return puts("-1"),0;
dp[0][0] = 1;
for (re int i = 1;i <= B;i++){
dp[i] = dp[i - 1];
for (re int j = 1;j <= n / 2;j++){
int val = j * (n - j);
if (val > k) break;
dp[i] |= (dp[i - 1] << val);
}
}
if (!dp[B][k]) return puts("-1"),0;
for (re int i = B,res = k;i;i--){
for (re int j = 0;j <= n / 2;j++){
int val = j * (n - j);
if (val > res) break;
if (dp[i - 1][res - val]){
cnt[i - 1] = j,res -= val;
break;
}
}
}
for (re int i = B - 1;~i;i--){
sort(ans + 1,ans + n + 1);
for (re int j = 1;j <= cnt[i];j++) ans[j] += (1ll << i);
}
for (re int i = 1;i <= n;i++){
if (ans[i] > m) return puts("-1"),0;
}
for (re int i = 1;i <= n;i++) printf("%lld ",ans[i]);
return 0;
}

浙公网安备 33010602011771号