2020年 CSP-S1 解析

djwj233

2021-08-26 20:56:31

Personal

这张卷子场上考了 $88$ 分,苟着过线,那么今年显然过不了了/cy 在洛谷博客上的排版可能会挂,凑合一下( ### 一、单项选择题 这年选择题十分简单。因为里面大多数都是思博题,因此只挑了几道出来。 > **10.** —个班学生分组做游戏,如果每组三人就多两人,每组五人就多三人,每组七人就多四人。 > > ​ 问这个班的学生人数 $n$ 在以下哪个区间?已知 $n<60$。( ) > > ​ A. $30<n<40$ B. $40<n<50$ C. $50<n<60$ D. $20<n<30$ 韩信点兵问题,本质上就是求解一个线性同余方程组。(当然直接枚举可能更快) 由于 $3,5,7$ 互质,因此可以用 CRT 解。 首先 $3\times 5\times 7=105$,然后有: $$ 5\times 7=35,\quad 35^{-1}\equiv2\!\!\pmod3 $$ $$ 3\times 7=21,\quad 21^{-1}\equiv1\!\!\pmod5 $$ $$ 3\times 5=15,\quad 15^{-1}\equiv1\!\!\pmod7 $$ 因此 $n$ 的通解为: $$ 2\times 35\times2 + 3\times21\times 1+4\times 15\times 1+105k\quad (k\in \mathbb Z) $$ 因此 $n$ 的最小正整数解为 $53$。选 **C**。 > **13.** 从一个 $4\times4$ 的棋盘中选取不在同一行也不在同一列上的两个方格,共有( )种方法。 > > ​ A. $60$ B. $72$ C. $86$ D. $64$ 排异法,求出所有方案数再减去在同一行或同一列的方案数。 $$ \binom{16}{2}-8\times\binom{4}{2}=72 $$ ### 二、阅读程序 这难度就离谱。 > **16.** > > ```c++ > #include <iostream> > using namespace std; > > int n; > int d[1000]; > > int main() { > cin >> n; > for (int i = 0; i < n; ++i) > cin >> d[i]; > int ans = -1; > for (int i = 0; i < n; ++i) > for (int j = 0; j < n; ++j) > if (d[i] < d[j]) > ans = max(ans, d[i] + d[j] - (d[i] & d[j])); > cout << ans; > return 0; > } > ``` > > 假设输入的 $n$ 和 $\texttt{d[i]}$ 都是不超过 $10000$ 的正整数,完成下面的判断题和单选题: > > - 判断题 > - **1)** $n$ 必须小于 $1000$,否则程序**可能**会发生运行错误。( ) > - **2)** 输出一定大于等于 $0$。( ) > - **3)** 若将第 13 行的 `j=0` 改为 `j=i+1` 程序输出**可能**会改变。 ( ) > - **4)** 将第 14 行的 `d[i]<d[j]` 改为 `d[i]!=d[j]`,程序输出**不会**改变。( ) > > - 单选题 > > - **5)** 若输入 $n$ 为 $100$,且输出为 $127$,则输入的 $\texttt{d[i]}$ 中不可能有( )。 > > - A. $127$ B. $126$ C. $128$ D. $125$ > > - **6)** 若输出的数大于 $0$,则下面说法正确的是( )。 > > - A. 若输出为偶数,则输入的 $\texttt{d[i]}$ 中**最多**有两个偶数 > > - B. 若输出为奇数,则输入的 $\texttt{d[i]}$ 中**至少**有两个奇数 > > - C. 若输出为偶数,则输入的 $\texttt{d[i]}$ 中**至少**有两个偶数 > > - D. 若输出为奇数,则输入的 $\texttt{d[i]}$ 中**最多**有两个奇数 首先,对于任意正整数 $a,b$,易证: $$ a\operatorname{or} b=a+b-(a\operatorname{and} b) $$ 那么我们发现程序是要读入一个数列,并输出**数列中任意两项按位或的最大值**。 - **1)**阅读程序可知,$n$ 可以等于 $1000$,选 **F**。(去年这里挂了) - **2)**注意到 `if(d[i]<d[j])` 这个条件,因此构造所有 $\texttt{d[i]}$ 都相等就能使答案为 $-1$,选 **F**。 - **3)**同上一题,可知双向都枚举才能保证答案正确,选 **T**。 - **4)**这样每个原来的答案会进行两次更新,最终还是等价的,选 **T**。 - **5)**$127<128$,因此不可能有 $128$,否则由于数列中不只有 $128$,按位或的值肯定不会小于 $128$。选 **C**。 - **6)**因为只有两个偶数的按位或和才是偶数,逐一排除可得答案为 **C**。 评价:坑点极多,总体难度不大。 > **17.** > > ```c++ > #include <iostream> > #include <cstdlib> > using namespace std; > > int n; > int d[10000]; > > int find(int L, int R, int k) { > int x = rand() % (R - L + 1) + L; > swap(d[L], d[x]); > int a = L + 1, b = R; > while (a < b) { > while (a < b && d[a] < d[L]) > ++a; > while (a < b && d[b] >= d[L]) > --b; > swap(d[a], d[b]); > } > if (d[a] < d[L]) > ++a; > if (a - L == k) > return d[L]; > if (a - L < k) > return find(a, R, k - (a - L)); > return find(L + 1, a - 1, k); > } > > int main() { > int k; > cin >> n; > cin >> k; > for (int i = 0; i < n; ++i) > cin >> d[i]; > cout << find(0, n - 1, k); > return 0; > } > ``` > > 假设输入的 $n,k$ 和 $\texttt{d[i]}$ 都是不超过 $10000$ 的正整数,且 $k$ 不超过 $n$。 > > 并假设 $\texttt{rand}$ 函数产生的是均匀的随机数,完成下面的判断题和单选题: > > - 判断题 > > - **1)**第 9 行的 $x$ 的数值范围是 $[L+1,R]$ 。( ) > - **2)**将第 19 行的 $\texttt{d[a]}$ 改为 $\texttt{d[b]}$,程序不会发生运行错误。( ) > > - 单选题 > > - **3)**当输入的 $\texttt{d[i]}$ 是严格**单调递增**序列时,第 17 行的 $\texttt{swap}$ 平均执行次数是( )。 > - A. $\mathcal O(n \log n)$ B. $\mathcal O(n)$ C. $\mathcal O(\log n)$ D. $\mathcal O(n^2)$ > - **4)**当输入的 $\texttt{d[i]}$ 是严格**单调递减**序列时,第 17 行的 $\texttt{swap}$ 平均执行次数是( )。 > - A. $\mathcal O(n^2)$ B. $\mathcal O(n)$ C. $\mathcal O(n\log n)$ D. $\mathcal O(\log n)$ > - **5)**若输入的 $\texttt{d[i]}=i$,此程序①平均的时间复杂度和②最坏情况下的时间复杂度分别是( )。 > - A. $\mathcal O(n)$, $\mathcal O(n^2)$ B. $\mathcal O(n)$, $\mathcal O(n \log n)$ > - C. $\mathcal O(n \log n)$, $\mathcal O(n^2)$ D. $\mathcal O(n \log n)$, $\mathcal O(n \log n)$ > - **6)**若输入的 $\texttt{d[i]}$ 都为同一个数,此程序平均的时间复杂度是( )。 > - A. $\mathcal O(n)$ B. $\mathcal O(\log n)$ C. $\mathcal O(n \log n)$ D. $\mathcal O(n^2)$ 时间复杂度分析四连击是全卷的重点之一,~~当年考的时候被吓傻了,但是都蒙对了~~。 差不多是一个`nth_element`的实现,在 $\texttt d$ 中找出第 $k$ 大数。 - **1)**由于`rand() % (R - L + 1)`取 $0$ 时 $x$ 可以取到 $L$,因此选 **F**。 - **2)**观察循环,此时一定有 $a=b$,选 **T**。 - **3)**实际上**选项有误**,正确答案为 $\mathcal O(\log^2 n)$。 做几次实验发现这个答案是对的,但是怎么证呢? 我们先看一个[错解](https://www.luogu.com.cn/paste/oslkyc7v)(我自己瞎写的,最后推出 $\mathcal O(\log n)$ 的结果 ~~可能出题人当时也是这么推的~~) 哪里错了呢?我们发现,排序过程中 $\texttt d$ 中的要处理的一段数**不一定**单调递减。 比如数列 $d=\{1,2,3,4,5\}$,取 $x=4$,变为 $d=\{4,2,3,1,5\}$。 现在模拟可知 $a=b=5$,若取左半边继续递归,则变成 $d^\prime=\{2,3,1\}$。 怎么办呢?我们发现,**一次递归至多在数列中增加一个不单调的数**。那么之后每次递归**至多**会在这个数上浪费一次 $\texttt{swap}$。 那么我们先用前面的错解求出期望递归深度 $T^\prime(n)=\mathcal O(\log n)$,然后用一个**费用提前计算**的思想列出: $$ T(n)=\dfrac{1}{n}\sum_{x=1}^n\left(\dfrac{x-1}{n}\left(T(x-1)+T^\prime(x-1)\right)+\dfrac{n-x}{n}\left(T(n-x)+T^\prime(n-x)\right)\right)+1 $$ 之后继续借鉴那个错解里的思路,摊开计算贡献: $$ T(n)=\dfrac{2}{n^2}\sum_{x=0}^{n-1} x(T(x)+T^\prime(x))+1 $$ 由于上面证明中我们发现 $T^\prime(x)=\Theta(\log n)$,那么我们设 $A(n)=T(n)+\Theta(\log n)$,忽略后面的常数 $1$,有: $$ A(n)=\dfrac{2}{n^2}\sum_{x=0}^{n-1} xA(x)+\Theta(\log n) $$ 另有: $$ A(n-1)=\dfrac{2}{(n-1)^2}\sum_{x=0}^{n-2} x(A(x)+\mathcal O(\log x))+\Theta(\log n-1) $$ 为约去分母,我们将两式同乘一个平方,然后**差分**: $$ n^2A(n)-(n-1)^2A(n-1)=\left(2\sum_{x=0}^{n-1} xA(x)+n^2\Theta(\log n)\right)-\left(2\sum_{x=0}^{n-2} xA(x)+(n-1)^2\Theta(\log n-1)\right) $$ $$ =2(n-1)T(n-1)+n^2\Theta(\log n)-(n-1)^2\Theta(\log n-1) $$ 移项并作近似处理得: $$ n^2A(n)=(n+1)(n-1)A(n-1)+n^2\log n-(n-1)^2\log (n-1) $$ 两边同除 $n(n+1)$: $$ \dfrac{nA(n)}{n+1}=\dfrac{(n-1)A(n-1)}{n}+\dfrac{n^2\log n-(n-1)^2\log (n-1)}{n(n+1)} $$ 设 $B(n)=\dfrac{nA(n)}{n+1}$,上式可化为: $$ B(n)=B(n-1)+\dfrac{n^2\log n-(n-1)^2\log (n-1)}{n(n+1)} $$ 简单归纳可得: $$ B(n)=\sum_{x=1}^n \dfrac{x^2\log x-(x-1)^2\log (x-1)}{x(x+1)} $$ 令 $f(x)=x^2\log x-(x-1)^2\log (x-1)$,我们需要探索 $f(x)$ 的性质。 构造函数 $g(x)=f(x)-2x\log x-x$,扔进 Geogebra 里画个图像发现 $g(x)$ 恒小于 $0$。 实际上,我们对 $g(x)$ 求导便可证明这一点,此处不作展开。 因此有 $f(x)=\mathcal O(x\log x)$。近似地有: $$ B(n)\sim \sum_{x=1}^n \frac{x\log x}{x^2}=\sum_{x=1}^n \frac{\log x}{x}\le \log n\sum_{x=1}^n \frac{1}{x}\sim \mathcal O(\log^2 n) $$ 于是 $A(n)=\mathcal O(\log^2 n)$,那么 $T(n)=\mathcal O(\log^2 n)$。 - **4)**在这里3)的假结论是正确的,要处理的一段数肯定是单调递减的。 那么我们发现对长度为 $n$ 的数列进行操作会带来 $\dfrac{n}{2}$ 次左右的 $\texttt{swap}$,那么我们直接用上面的方法差分即可。 $$ T(n)=\dfrac{1}{n}\sum_{x=1}^n\left(\dfrac{x-1}{n}T(x-1)+\dfrac{n-x}{n}T(n-x)\right) +\frac{n}{2} $$ $$ T(n)=\dfrac{2}{n^2}\sum_{x=0}^{n-1} xT(x) +\frac{n}{2} $$ $$ T(n-1)=\dfrac{2}{(n-1)^2}\sum_{x=0}^{n-2} xT(x)+\frac{n-1}{2} $$ $$ n^2T(n)-(n-1)^2T(n-1)=\left(2\sum_{x=0}^{n-1} xT(x)+\frac{n^3}{2}\right)-\left(2\sum_{x=0}^{n-2} xT(x)+\frac{(n-1)^3}{2}\right) $$ $$ =2(n-1)T(n-1)+\frac{3}{2}n^2-\frac{3}{2}n+\frac{1}{2} $$ $$ n^2T(n)=(n+1)(n-1)T(n-1)+\frac{3}{2}n^2-\frac{3}{2}n+\frac{1}{2} $$ $$ \dfrac{nT(n)}{n+1}=\dfrac{(n-1)T(n-1)}{n}+\dfrac{3n^2-3n+1}{2n(n+1)} $$ 设 $B(n)=\dfrac{nT(n)}{n+1}$: $$ B(n)=\sum_{x=1}^n \dfrac{3x^2-3x+1}{2x(x+1)}=\sum_{x=1}^n\mathcal O(1)=\mathcal O(n) $$ $$ T(n)=\dfrac{n+1}{n} B(n)=\mathcal O(n) $$ 选 **B**。 - **5)**平均时间复杂度就是下式: $$ T(n)=\dfrac{1}{n}\sum_{x=1}^n\left(\dfrac{x-1}{n}T(x-1)+\dfrac{n-x}{n}T(n-x)\right) + n $$ 随便推推可得 $T(n)=\mathcal O(n)$。 最坏情况下,每次的分割点都取到边缘位置,此时有: $$ T(n)=T(n-1)+n $$ 那么: $$ T(n)=\sum_{x=1}^n x=\mathcal O(n^2) $$ 选 **A**。 - **6)**模拟程序过程,发现如果所有 $\texttt{d[i]}$ 都相同,那么在第一次循环时 $a$ 会停在 $L+1$ 处,$b$ 会一直左移至 $L+1$ 处。 因此就相当于每次花 $\Theta(n)$ 的时间把数列切出来一个数。因此,如果询问排名为 $k$ 的数,复杂度即为: $$ \sum_{x=k}^nx=\frac{(n-k+1)(n-k+2)}{2} $$ 因此平均时间复杂度为: $$ T(n)=\frac{1}{n}\sum_{k=1}^n \frac{(n-k+1)(n-k+2)}{2}=\frac{1}{n}\sum_{x=1}^n\frac{x^2+x}{2}=\mathcal O(n^2) $$ 选 **D**。 总结:大毒瘤。 (以下为**基于经验主义**的战术胡扯)一般而言,对于时间复杂度(快排求 K 大值) $$ T(n)=\dfrac{1}{n}\sum_{x=1}^n\left(\dfrac{x-1}{n}T(x-1)+\dfrac{n-x}{n}T(n-x)\right) + f(n) $$ 其时间复杂度相当于 $$ T(n)=T(\frac{n}{2})+f(n) $$ 而对于时间复杂度(快排) $$ T(n)=\dfrac{1}{n}\sum_{x=1}^n\left(T(x-1)+T(n-x)\right) + f(n) $$ 其时间复杂度相当于 $$ T(n)=2T(\frac{n}{2})+f(n) $$ > **18.** > > ```c++ > #include <iostream> > #include <queue> > using namespace std; > > const int maxl = 20000000; > > class Map { > struct item { > string key; int value; > } d[maxl]; > int cnt; > public: > int find(string x) { > for (int i = 0; i < cnt; ++i) > if (d[i].key == x) > return d[i].value; > return -1; > } > static int end() { return -1; } > void insert(string k, int v) { > d[cnt].key = k; d[cnt++].value = v; > } > } s[2]; > > class Queue { > string q[maxl]; > int head, tail; > public: > void pop() { ++head; } > string front() { return q[head + 1]; } > bool empty() { return head == tail; } > void push(string x) { q[++tail] = x; } > } q[2]; > > string st0, st1; > int m; > > string LtoR(string s, int L, int R) { > string t = s; > char tmp = t[L]; > for (int i = L; i < R; ++i) > t[i] = t[i + 1]; > t[R] = tmp; > return t; > } > > string RtoL(string s, int L, int R) { > string t = s; > char tmp = t[R]; > for (int i = R; i > L; --i) > t[i] = t[i - 1]; > t[L] = tmp; > return t; > } > > bool check(string st , int p, int step) { > if (s[p].find(st) != s[p].end()) > return false; > ++step; > if (s[p ^ 1].find(st) == s[p].end()) { > s[p].insert(st, step); > q[p].push(st); > return false; > } > cout << s[p ^ 1].find(st) + step << endl; > return true; > } > > int main() { > cin >> st0 >> st1; > int len = st0.length(); > if (len != st1.length()) { > cout << -1 << endl; > return 0; > } > if (st0 == st1) { > cout << 0 << endl; > return 0; > } > cin >> m; > s[0].insert(st0, 0); s[1].insert(st1, 0); > q[0].push(st0); q[1].push(st1); > for (int p = 0; > !(q[0].empty() && q[1].empty()); > p ^= 1) { > string st = q[p].front(); q[p].pop(); > int step = s[p].find(st); > if ((p == 0 && > (check(LtoR(st, m, len - 1), p, step) || > check(RtoL(st, 0, m), p, step))) > || > (p == 1 && > (check(LtoR(st, 0, m), p, step) || > check(RtoL(st, m, len - 1), p, step)))) > return 0; > } > cout << -1 << endl; > return 0; > } > ``` > > - 判断题 > > - **1)**输出**可能**为 $0$。( ) > - **2)**若输入的两个字符串长度均为 $101$,则 $m=0$ 时的输出与 $m=100$ 时的输出是一样的。( ) > - **3)**若两个字符串的长度均为 $n$,则最坏情况下,此程序的时间复杂度为 $\mathcal O((n!)^2)$。( ) > > - 选择题 > > - **4)**若输入的第一个字符串由 $100$ 个不同的字符构成,第二个字符串是第一个字符串的倒序,输入的 $m$ 为 $0$,则输出为( )。 > > - A. $49$ B. $50$ C. $100$ D. $-1$ > > - **5)**己知当输入为 $\texttt{0123{\color{red}\texttt{\n}}3210{\color{red}\texttt{\n}}1}$ 时输出为 $4$,当输入为 $\texttt{012345{\color{red}\texttt{\n}}543210{\color{red}\texttt{\n}}1}$ 时输出为 $14$,当输入为 $\texttt{01234567{\color{red}\texttt{\n}}76543210{\color{red}\texttt{\n}}1}$ 时输出为 $28$,则当输入为 $\texttt{0123456789ab{\color{red}\texttt{\n}}ba9876543210{\color{red}\texttt{\n}}1}$ 时输出为 > ( )。其中 ${\color{red}\texttt{\n}}$ 为换行符。 > > - A. $56$ B. $84$ C. $102$ D. $68$ > > - **6)**若两个字符串的长度均为 $n$,且 $0<m<n-1$,且两个字符串的构成相同(即任何一个字符在两个字符串中出现的次数均相同),则下列说法**正确**的是( )。 > > (提示:考虑输入与输出有多少对字符前后顺序不一样。) > > - A. 若 $n,m$ 均为奇数,则输出**可能**小于 $0$。 > - B. 若 $n,m$ 均为偶数,则输出**可能**小于 $0$。 > - C. 若 $n$ 为奇数、$m$ 为偶数,则输出**可能**小于 $0$。 > - D. 若 $n$ 为偶数、$m$ 为奇数,则输出**可能**小于 $0$。 当年考的时候三道选择题全蒙错了/tuu 逐步分析代码,发现代码中实现了一个 $\mathcal O(n)$ 的 map 和一个 queue。 $\texttt{LtoR}$ 函数起**把字符串 $L$ 位置的字符移至 $R$ 处,原先 $(L,R]$ 中的字符依次前移**。$\texttt{RtoL}$ 函数同理。 $\texttt{check}$ 函数则是用于检查询问的字符串是否在另一个 map 内出现,若出现则输出二者的值之和并退出程序。 现在观察主函数,我们发现这是一个借助 queue 执行的 BFS 的过程。 BFS 中,每次把 $\texttt{st0}$ 中 **$m$ 位置的字符移到头部或尾部**,或把 $\texttt{st1}$ 中**头部或尾部的字符移到 $m$ 位置**。 那么,答案就是**二者经过最小轮数的操作达到相同的总操作数**。 由于操作作用在 $\texttt{st0}$ 上和作用在 $\texttt{st1}$ 上是对称的,我们发现实际上答案就是**使二者相同的最小的总操作数**。 - **1)**观察第 72~75 行,若 $\texttt{st0}=\texttt{st1}$,那么就会输出 $0$。选 **T**。 - **2)**$m=0$ 时相当于只允许 $\texttt{st0}$ 进行全局 $\texttt{LtoR}$,只允许 $\texttt{st1}$ 进行全局 $\texttt{RtoL}$,而 $m=100$ 时正好相反。 ​ 故选 **F**。 - **3)**最坏情况就是产生了所有可能的排列,共 $\mathcal O(n!)$ 个。 这样每次插入时都要花 $\mathcal O(n!)$ 的时间枚举 map 中的字符串,对每个都进行 $\mathcal O(n)$ 的比较。 总复杂度 $\mathcal O((n!)^2\cdot n)$。 - **4)**手动模拟 $len=3$ 的情况就能知道无法使二者达到相同。 事实上,全局 $\texttt{LtoR/RtoL}$ 所得的所有串是与原串循环同构的,而 $len>2$ 时原串与反串显然不满足循环同构。 因此会输出 $-1$ 表示不可能,选 **D**。 - **5)**乱搞题。由于给了我们三个点值,我们考虑插值,可以插出一个二次函数。 代入 $(2,4),(3,14),(4,28)$ 可得 $y=2x^2-4$。 然后代入 $x=6$ 可以得到答案 $68$。选 **D**。 - **6)**我们把 $\texttt{st1}$ 看作正序,进行映射。由于双向搜索的特性,我们可以钦定只有 $\texttt{st1}$ 发生了变化。 则若 $n$ 为奇数、$m$ 为偶数则每次交换逆序对数量**只能减少一个偶数**,而不能改变其奇偶性。 为啥只减少一个偶数捏?我们假设 $\texttt{RtoL}$ 时前面有 $x$ 个比它小的,$y$ 个比它大的。易知 $x+y$ 是偶数,因此二者奇偶性相同。 对 $x$ 个比它小的来说,将它前移共会增加 $x$ 个逆序对;对 $y$ 个比它大的来说,将它前移共会减少 $y$ 个逆序对。 因此 $x-y$ 是偶数,$\texttt{LtoR}$ 时同理。选 **C**。 评价:离谱。 ### 三、完善程序 这部分倒是都对了。 **19.** 直接贪心,略。 > **20.** 懒得修 $\LaTeX$ 了,题面自己看去。 > > ```c++ > #include <iostream> > > using namespace std; > > typedef long long LL; > > const int MAXN = 40000, M = 16, B = M >> 1, MS = (1 << B) - 1; > const LL INF = 1000000000000000LL; > LL Max[MS + 4][MS + 4]; > > int w(int x) > { > int s = x; > while(x) > { > __(1)__; > s++; > } > return s; > } > > void to_max(LL &x, LL y) > { > if(x < y) > x = y; > } > > int main() > { > int n; > LL ans = 0; > cin >> n; > for(int x = 0; x <= MS; x++) > for(int y = 0; y <= MS; y++) > Max[x][y] = -INF; > for(int i = 1; i <= n ; i++) > { > LL a; > cin >> a; > int x = __(2)__ , y = a & MS; > LL v = __(3)__; > for(int z = 0; z < = MS; z++) > to_max(v, __(4)__); > for(int z = 0; z < = MS; z++) > __(5)__; > to_max(ans , v); > } > cout << ans << endl; > return 0; > } > ``` > > 1. A. `x >>= 1` B. `x ^= x&(x ^ (x + 1))` C. `x -= x | -x` D. `x ^= x &(x ^ (x - 1))` > > 2. A. `(a & MS) << B` B. `a >> B` > > C. `a & (1 << B)` D. `a & (MS << B)` > > 3. A. `-INF` B. `Max[y][x]` C. `0` D. `Max[x][y]` > > 4. A.`Max[x][z] + w(y ^ z)` > > B.`Max[x][z] + w(a ^ z)` > > C.`Max[x][z] + w(x ^ (z << B))` > > D.`Max[x][z] + w(x ^ z)` > > 5. A. `to_max(Max[y][z], v + w(a ^ (z << B)))` > > B.`to_max(Max[z][y], v + w((x ^ z) << B))` > > C.`to_max(Max[z][y], v + w(a ^ (z << B)))` > > D.`to_max(Max[x][z], v + w(y ^ z))` 赛后了解到这好像叫什么平衡规划。 主要思想是转移时保留一半**先前的信息**和一半**之后的信息**,不过只能适用于贡献可拆分的情况。 **1.** 这是一个求 $\mathrm{popcnt}$ 的过程,每次从 $x$ 中去掉它的 $\textrm{lowbit}$。选 **D**。 **2.** 由于 $y$ 是 $a$ 的后 $B$ 位,自然猜测 $x$ 是 $a$ 的前 $B$ 位。选 **B**。 **3.** 主要考虑到 $i=1$ 时所有转移答案都是 $-\infty$,因此需要有一个 $0$ 保底。 **4.** 这是要计算当前的最大答案,算出当时的后 $B$ 位的权值。选 **A**。 **5.** 算出前 $B$ 位的贡献来计算之后的答案。选 **B**。 评价:就这???