NOIP 模拟赛:2024-9-30

为什么会有傻子每次计算都初始化线段树一次 …… st = SegmentTree(n) 改成 st.mdf(1, n + 1, -1) 就 += 25pts 了……

T1

大力分讨题。

首先容易观察到,当 \(|a|=|b|\)\(a,b\) 都不含 \(*\) 的时候,\(T\) 不含 \(*\)。此时的 \(T_i=?\) 当且仅当 \(a_i\neq b_i\)\(a_i=b_i=?\),否则 \(T_i=a_i=b_i\)

因为 \(T=*\) 一定满足条件,所以我们断定 \(T\)\(*\) 的个数 \(\le 1\);又在下面的情况中,\(a\)\(b\) 的长度可能不同,所以 \(T\) 必然包含 \(*\)。因此下面两种情况的 \(T\) 恰好包含一个 \(*\)
观察发现 \(|T|\) 等于 \(a,b\) 去掉 \(*\) 后长度较小值 \(+1\),且形式必然能写成 \(T=T1+*??\dots ??+T2\)\(T1\) 是把 \(a\) 的一个前缀和 \(b\) 的前缀匹配,\(T2\) 是把 \(a\) 的一个后缀和 \(b\) 的后缀匹配。注意匹配的前缀和后缀显然在 \(a,b\) 中都不能带 \(*\)

预处理 \(pfx_i,suf_i\),分别表示 \(a[1\sim i],a[i\sim |a|]\)\(b[1\sim i],b[|b|-|a|+i\sim |b|]\) 匹配,会产生多少个括号。如果涉及 \(*\) 就标记 \(+\infty\)

然后大体的思路就是求出 \(a,b\) 各自最左侧、最右侧的 \(1\) 位置,枚举 \(a\) 前缀 \(i\) 个字符与 \(b\) 的前缀匹配了,根据 \(1\) 的位置可以算出 \(a\)\(b\) 匹配的后缀会有多少个字符。可以用 \(pfx,suf\) 算出一个方案所需的 \(?\) 个数,在所有方案中找 \(?\) 个数最小的输出。但是细节极多。

  1. \(ans=T1+*??\dots ??+T2\),若 \(ans\)\(*\) 的前面有 \(?\),要交换位置。

  2. 记得处理 \(a\) 没有前缀与 \(b\) 匹配的情况。

  3. 为了使 \(*\) 尽量靠前,若 \(a\)\(i_1,i_2\) 个前缀与 \(b\) 的前缀匹配加上对应后缀的 \(?\) 个数,得出的 \(?\) 个数相等,应当取较小的那一个。

T2

类似上一场的 trick,筛法求质数。对于每个质数求最长的段,使得段内 \(1\) 的个数 \(\ge len/2\)。原始的想法是枚举两个 \(1\) 的位置 \(p_x,p_y(x>y)\),若 \(p_x-p_y+1\le 2\cdot (x-y+1)\) 就可行。然后在可行的基础上先尽量往左拓展,再尽量往右拓展。

注意到如果 \(p_x\) 这个 \(1\)\(p'_1,p'_2,\dots\) 这些位置的 \(1\) 都可行,必然取距离 \(p_x\) 最远的 \(1\) 是最小的。考虑枚举 \(i\) 为右侧 \(1\) 的位置,找到距离 \(i\) 最远的合法 \(1\) 的位置。

转化一下合法的要求,得到 \(p_x-2x\le p_y-2y+1\)。用一个线段树支持单点加和区间查询最小值,维护 \(p_i-2i\) 即可。用 BIT 也可以。

T3

性质:存在分界点,右边选的 \(a_i\) 递减,且只有右边的 \(b_i\) 贡献答案。

于是把所有 pair 按 \(a\) 从大到小排序。

一个粗略的想法是 \(dp[i][j]\) 表示前 \(i\) 个 pair 任意分到左边右边,使得左边的 \(\sum a=j\) 的右边 \(b_i\) 减去对应的 \(x\) 最大是多少。转移通过枚举 \(i+1\) 是去左边还是右边。但是发现枚举 \(i+1\) 去左边的时候,会使得原本右边每个数的贡献都发生变化,不好处理。

想一想,这个分界点是很重要的。它决定了右边开始算贡献时的初始 \(x\)。因此 70pts 的想法就是枚举这个分界点的 \(x=x0\),然后按上面的想法 DP,但算右侧贡献的时候默认分界点的 \(x=x0\)。然后统计答案时只统计 \(dp[n][x-x0\sim x]\)


100pts:假设当前循环到第 \(i\) 个 pair,可以把所有数分成三个部分,已经决策了放在左边的、还没决策但未来会在左边的、已经决策了放在右边的、还没决策但未来会放在右边的。把这四类记作 \(A,B,C,D\) 类。

要使得决策一个点去右边的时候,我们能知道这个点的 \(b\) 会产生多少贡献,我们必须知道此时这个点上一个的 \(x\) 是多少。因此 \(dp[i][x]\) 表示决策了前 \(i\) 个 pair,\(X\) 减去 \(A,B,C\) 类的 \(a\) 和 等于 \(j\) 的最大贡献。

注意这里我们把 \(B\) 类也考虑了,也就是把未来可能会放在左边的数也考虑了。

然后是转移。暂且不考虑 \(X\) 减到 \(0\) 的特殊情况。

\(i\) 放在右边,\(dp[i][x]\leftarrow dp[i-1][x+a_i]+b_i-x\)。这很好理解。

\(i\) 放在左边,\(dp[i][x]\leftarrow dp[i-1][x]\)。因为当 \(i\) 决策放在左边,在 \(i-1\) 的视角,\(i\) 实际是属于 \(B\) 类的,\(a_i\) 已经累加进 \(i-1\)\(x\) 里面了。

这么 DP 就能做了。但是有几个细节。

如何处理 \(x=0\)?因为当 \(x=0\) 时,它的前置状态是不确定的。单独用一重循环枚举当然可以,但是这里有个更方便的方法:改状态定义。把状态定义改成 \(dp[i][b]\) 表示前 \(i\) 个,\(B\) 类的 \(a\) 和等于 \(b\) 的最大贡献。那么此时 \(i\) 面对的 \(x\) 就是 \(\max(0,X-pfx_i-b)\),是容易求的。

这样不会多统计答案吗?实际会有不合法(也就是不满足左边不贡献,右边全贡献的方案)出现,但是这些方案都比实际的最优更劣,因此不会被统计。

posted @ 2024-10-05 22:15  FLY_lai  阅读(42)  评论(0)    收藏  举报