思路
构造,且上界并不是特别严格。/bx/bx/bx
首先加法比较“混合”,考虑转成位运算,具体地,钦定操作的 \(a, b\) 满足 \(a\&b = 0\)。
考虑递归成子问题,按照 popcount 分组,有一个关键观察是:我们在操作一个 \(a|b = c\) 的时候,可以将任意几个 \(d\&c = d\) 且 \(popcount(d) = popcount(c) - 1\) 的 \(a_d\) 01 翻转。
具体而言,假设我们在操作 10111,我们要使得 10110 和 10101 01翻转,那我们可以使 a|b = 10111,且 b = 00011,即我们要的 \(d\) 的缺的 1。
发现这个时候 < k - 1 的位上的 a 会受到一堆操作的影响,那我们直接先不管它,因为我们在操作 \(popcount = i\) 的时候 \(popcount > i\) 的是不会受影响的所以递归成子问题即可。
那么我们考虑求出 \(T_{1\sim k}\) 使得 \(T_i\) 能覆盖所有 \(popcount = i - 1\)。这个可以贪心求,每次求最大的可行的即可(可以用桶优化),不一定最优但是够了,我构造出来的 \(\sum T_k = 159599\),不知道为啥,其他人好像可以构造出 157884。哦,他们选择的是覆盖最多的情况下最小的值加入 T 中,而我是随便选的,不知道为什么他们会更优/yun
好像没什么细节,注意一开始若 \(a_{n - 1}\not= 1\) 的话要额外进行一次操作使得 \(a_{n - 1}\) 变为 1.
代码
const int N = 1 << 20;
const int LogN = 20;
int n, k, _, tot;
int a[N], vis[N], cnt[N], inT[N];
vector <int> pos[LogN + 1], val[LogN + 1], T[LogN + 1];vector <pii> opt;
void answer(int a, int b) {assert((a & b) == 0);int s = b;do {Main::a[a | s] ^= 1;} while(s && (s = (s - 1) & b, 1));opt.eb(a, b);
}
void output() {printf("%llu\n", opt.size());for (pii _ : opt) {int a = _.fi, b = _.se;printf("%d %d\n", a, b);}fflush(stdout);
}void skymaths() {read(n, k, _);for (int i = 0; i < n; ++i) read(a[i]);// read(k); n = 1 << k;for (int i = 0; i < n; ++i) {vis[i] = 1;pos[cnt[i] = __builtin_popcount(i)].emplace_back(i);}rep (c, 1, k) {// 求 T(c),即覆盖所有 T(c - 1)for (int i = 0; i <= k; ++i) val[i].clear();for (int v : pos[c]) {val[c].eb(v);assert(cnt[v] == c);// printf("c = %d, v = %d\n", c, v);}per (i, k, 1) {for (int v : val[i]) {if (cnt[v] != i) {// printf(" %d cnt -> %d\n", v, cnt[v]);// assert(cnt[v] < i);val[cnt[v]].eb(v);continue;}T[c].eb(v); ++tot;inT[v] = 1;rep (j, 0, k - 1) {if ((v >> j & 1) && vis[v ^ (1 << j)]) {int t = v ^ (1 << j);vis[t] = 0;rep (bit, 0, k - 1) {if (t >> bit & 1) continue;--cnt[t ^ (1 << bit)];}}}}}}// 此时输出 tot 就是 sum |Tk|if (!a[n - 1]) answer(n - 1, 0);per (i, k, 1) {for (int v : T[i]) {int msk = 0;rep (j, 0, k - 1) {if (v >> j & 1) {if (a[v ^ (1 << j)] != inT[v ^ (1 << j)]) msk |= 1 << j;}}answer(v ^ msk, msk);}}rep (i, 0, n - 1) {if (a[i] != 0) {cerr << "Wrong on i = " << i << endl;}}output();// check 正确性用的代码// clr(vis);// rep (c, 1, k) {// for (int v : T[c]) {// rep (j, 0, k - 1) {// if (v >> j & 1) {// vis[v ^ (1 << j)] = 1;// }// }// }// }// rep (i, 0, n - 1) {// if (!vis[i]) {// printf("wrong on i = %d\n", i);// }// }// printf("%d\n", tot);// return ;// rep (c, 1, k) {// printf("T(%d) = \n", c);// for (int v : T[c]) {// printf("%d ", v);// }// printf("\n");// }
}