CSP-J 普及组

A. number

  • 题意:输入一行字符串s,用其中出现的数字最大能拼出多少。
  • 题解:找出所有数字后,排序或者直接桶排,倒序输出所有数字。注意特判 $0$。

B. seat

  • 题意:一群人按照分数高低蛇形坐座位,求分数为 $x$ 人坐在哪里。
  • 题解:对分数排序,在二维数组上模拟填入即可。然后循环找到坐标。

C. xor

  • 题意:给定序列 $a$,求最多能选出多少个不交区间,满足区间异或均为 $K$。
  • 题解:
    • 需要注意到异或具有可加减性,即 $a\oplus b\oplus a=b$。
    • 所以可以适用前缀和,处理出 $s_i$ 表示 $[1,i]$ 的异或,$s_r \oplus s_{l-1}$ 即 $[l,r]$ 的异或。
    • 注意到对于任意 $x$,存在唯一非负整数 $y$ 满足 $x\oplus y=k$,且 $y=k\oplus x$。
    • 对于某个特定位置 $i$,贪心地选择最靠右的 $j$ 满足 $s_j=s_i\oplus k$ ,将 $[j+1,i]$ 纳入考虑。
      • 选择最靠右的 $j$ 是因为靠左的 $j_0$ 包含区间 $[j+1,i]$,因此不优。
      • 纳入考虑的意思是 $[j+1,i]$ 可能出现在最终答案里。
    • 于是得到了 $O(n)$ 条可能的线段。把这些线段按右端点排序,如果可以就贪心选择即可。
      • 其实如果能够想到这里直接 $O(n)$ 扫过去也是可以做的。

D. polygon

  • 题意:给定序列 $a$,求 $a$ 有多少个子序列 $b$ 满足 $\sum b >2\times \max b$ 。
  • 题解:
    • 求子序列的问题大多可以考虑 dp,且子序列与原数组顺序无关,可以直接排序。
    • 考虑设计 dp 状态,类似于背包,我们需要记录下每一种情况。
    • 设 $f_{i,j}$ 表示考虑了 $[1,i]$,子序列和为 $j$ 的方案数。如果不记录和不能转移,所以 $j$ 省不掉。
    • 但是注意到不等式右侧,可以理解为 $\sum b(除了最大值)>\max b$ ,右侧 $\leq 5000$。
    • 事实上有用的 $j$ 仅仅为 $[0,5001]$。
    • 因此直接类似背包转移即可:
      • $f_{i,j}\leftarrow f_{i-1,j}$
      • $f_{i,j}\leftarrow f_{i-1,j-a_i}$
    • 需要注意当 $j>5001$ 时所有转移混在一起,相当于后面的方案翻倍。令 $w=5001$ :
      • $f_{i,w}\leftarrow f_{i-1,j},j\in[w-a_i,w]$
      • $f_{i,w}\leftarrow 2f_{i-1,w}$
    • 然后枚举子序列右端点 $i$,将 $f_{i-1,j},j\in[a_i+1,w]$ 加起来即可。

CSP-S 提高组

A. club

  • 题意:给定 $n$ 个选项,第 $i$ 个选项可以选择价值 $a_i,b_i,c_i$ 加入得分。要求同类选项不得超过 $\frac{n}{2}$ 个。保证 $n$ 为偶数,求最大得分。
  • 题解:
    • 考虑经典反悔机制,先选择每个选项中最大的加入得分。
    • 然后考虑如果方案不合法,一定会出现一个唯一的众数(不超过 $\frac{n}{2}$ 且 $n$ 为偶数的原因)
    • 把这个唯一众数的选项分到其它选项上,直到唯一众数恰好等于 $\frac{n}{2}$。
    • 由于唯一众数最终等于 $\frac{n}{2}$,一定不会出现新的众数。
    • 按反悔后的代价排序以后贪心即可。

B. road

  • 题意:给定 $n$ 点 $m$ 边的无向带权图,以及 $k$ 个中转点,中转点 $i$ 向每一个 $j$ 连出一条权值为 $a_{i,j}$ 的边,但是使用第 $i$ 个中转点需要额外的 $c_i$ 权值,求最小生成树。
  • 题解:
    • 最小生成树一般离不开 kruskal,或者其衍生的其他算法(B算法)。
    • 考虑如果不考虑中转点容易建出一棵最小生成树 $T_1$。
    • 考虑如果一条边不在 $T_1$ 里,加入了中转点后它仍然不会在 $T_1$ 里,因为多出来的边只会使得连通块数减小,越不可能使得本来就在一个连通块内的两个点出现在不同连通块内。
    • 所以我们将之后的考虑范围从 $m$ 条边缩减到至多 $n-1$ 条边。
    • 由于中转点的额外花费会严重破坏 kruskal 的贪心过程,因此大概率需要 $O(2^k)$ 枚举。
    • 如果知道了要选择哪些点,用对应的 $O(kn)$ 条边和之前的 $n-1$ 条边跑一遍即可。
    • 复杂度 $O(m\log m+2^kkn\alpha (n))$,注意提前把边排序,否则会因为多一个 $\log$ 超时。

C replace

前置知识:trie+(树上差分 或 AC自动机)

  • 题意:给定 $n$ 个替换条件表示把字符串 $s_{i,1}$ 完整替换为 $s_{i,2}$ 。$q$ 次询问 $t_{i,1},t_{i,2}$,求有多少个替换条件,将其应用在 $t_{i,1}$ 的某个区间上可以替换为 $t_{i,2}$ 。
  • 题解:
    • 考虑将 $s_{i,1}\rightarrow s_{i,2}$ 视为 $ACB\rightarrow ADB$,大写字母表示一个串。
    • 考虑如果应用了某对 $s$ 使得 $t_{i,1} \rightarrow t_{i,2}$ ,一定形如 $ECF\rightarrow EDF$ 。
      • 中间的 $C,D$ 为第一个和最后一个不同位置围起来的串。
      • $A$ 是 $E$ 的后缀,$B$ 是 $F$ 的前缀。
      • 如果最后的 $t$ 不是这样的形式,则会把原本相等的改成不同的,矛盾。
    • 于是按照 $(C,D)$ 的二元组将 $s$ 分类,这里可以使用哈希或者 trie。
    • 接下来有如下做法,任意取一种即可。
      • 对每一个分类按照 $A?B$ 建立AC自动机,每次询问相当于求有多少个不同的 $A?B$ 在 $E?F$ 中出现,$?$ 用来对齐串的位置。跑模板即可。
      • 对每一个分类建立 $A$ 的后缀字典树 $T_1$ 和 $B$ 的前缀字典树 $T_2$,然后每次询问相当于给定$T_1,T_2$ 上从根出发的两条链,求同时出现在两条链上的 $s$ 串有多少个,可以离线询问然后对每一组单独求取。但需要注意时间复杂度和细节,不要多 $\log$。

D employ

  • 题意:有 $n$ 个人面试,你需要安排面试的顺序,每一天有一个人面试。如果第 $i$ 个人前面有多于 $c_i$ 个人失败,则他会直接跑路并视为面试失败。给定有若干天的面试一定会失败。求有多少种顺序使得最终有至少 $m$ 人成功。
  • 题解:
    • 考虑dp,设 $f_{i,j}$ 表示前 $i$ 天,拒绝了 $j$ 个人的方案数。
    • 考虑到目前仍然没有办法转移,难点主要在于 $c_i$ 的顺序太乱,没法约束。
    • 考虑可以引入新的一维强制 $c_i$ 有序地从低到高转移,考虑到 $c$ 与 $j$ 强相关,可以每次将前面一些位置留空但是强制钦定通过,然后将每次将 $c_x=j$ 的人填入。
    • 预处理 $t_i$ 表示 $c=i$ 的人数,$p_i$ 为 $t_i$ 的前缀和。
    • $f_{i,j,k}$ 表示前 $i$ 天,拒绝了 $j$ 个人,前面还有 $k$ 个位置需要补位:
      • $f_{i,j,k+1}\leftarrow f_{i-1,j,k}$,(留空一个面试成功的位置)。
      • $f_{i,j,k+1-x}\leftarrow f_{i-1,j-1,k}\binom{k+1}{x}\binom{t_{j}}{x}x!$ ,(选择把 $c_z=j$ 的 $x$ 个补位)。
      • $f_{i,j,k-x} \leftarrow f_{i-1,j-1,k}(p_{j-1}-(i-k))\binom{k}{x}\binom{t_{j}}{x}x!$,(选择一个跑路的人补位)。
    • 枚举最终有多少人失败:
      • $ans=\sum_{j=0}^{n-m}f_{n,j,n-p_j}(n-p_j)!$ 。