0906训练

A - Destroy the Colony

首先发现,英文字母一共只有 \(26\) 个,算上大小写,\(|\Sigma|=52\)。那么我们就相当于是挑选一个字母子集,使得其总和为 \(n/2\),然后分别全排列。

但是我们发现,最后的全排列是和前面的子集没有关系的,因为根据多重集,答案等于 \(\dfrac{(n/2)!}{\prod_{i\in A} (cnt_i!)}\dfrac{(n/2)!}{\prod_{i\in B} (cnt_i!)}\),也就等于 \(\dfrac{((n/2)!)^2}{\prod_{i} (cnt_i!)}\)

所以,实际上我们只需要求划分方案的方案数。

那么,不考虑后面的限制,我们显然可以背包,最终就是要求 \(dp_{n/2}\)

如果 \(x\)\(y\) 的类要求在同一半里面呢?

我们显然是需要求“不使用 \(x\)\(y\),选出若干字符,大小总和是 \(n/2-cnt_x-cnt_y\)”的方案数。而这样就可以回撤背包。具体的,我们强制把 \(x\)\(y\) 放在字符集的最后,然后通过 \(dp_{|\Sigma|}\) 倒推 \(dp_{|\Sigma|-1}\)\(dp_{|\Sigma|-2}\),答案就是 \(dp_{|\Sigma|-2,n/2-cnt_x-cnt_y}\)

这样做的复杂度是 \(O(nq)\) 的。但是我们发现字符集大小只有 \(52\),完全可以预处理所有的组合,复杂度 \(O(|\Sigma|^2n+q)\)

B - Unambiguous Arithmetic Expression

首先考虑区间 \(dp\),设 \(dp_{l,r}\) 表示当前处理到区间 \([l,r]\) 变成一个 UAE 的方案数。

然后,我们发现这样做是 \(O(n^3)\) 的,但是有一些优化。

首先,我们得把所有的数字压成一位。

其次,右端点是数字的状态才是合法的,剩下的都不合法,可以删掉。

然后,为了转移点是符号,我们可以把所有的数字和符号的位置存起来,然后直接在数组上遍历,免除无用的查询。

最后,因为一次转移的所有答案之和即使不取模也不会爆 long long,所以完全可以先都压进 long long 里,到最后再取模。

这样就可以跑过去了。

考虑 \(dp\),设 \(dp_{i,j}\) 表示 \(dp\) 到第 \(i\) 位,目前有 \(j\) 个左括号需要配对的方案数。

但是这样就面临一个位置可能有多个左括号或右括号的问题。而 \((((x)))\) 这样的式子又是显然不合法的。

我们考虑将括号和符号唯一匹配,但是这样面临的问题就是 \((x)+(y)\),中间的加号要面临两对括号。

这样就有一个常见的 \(trick\),我们把符号左边的括号拆开,不难看出,只要我们知道每个加号右边的括号范围,就能对应推出它被左边覆盖的范围,所以能够唯一的还原成有左边括号的情形。

然后我们发现,每个左括号就只会在符号后出现,且一次只能出现一个,强制出现,而右括号在数字后出现,一次可以出现若干个。

这样就可以 \(dp\),每次遇到在符号后面的位置,就强制第二维 \(+1\),在数字后面的位置,就 \(dp_{i+1,j}=dp_{i+1,j+1}+dp_{i,j}\) 的递推下去即可。

复杂度 \(O(n^2)\)

C - Good Pairs

我们发现,如果路径不是单调的,完全可以删掉不单调的位置使其单调。

那么就只有两种情况:单调增、单调减、两端相同。

两端相同可以特判,单调减可以翻转序列变成单调增。

所以我们只需要统计区间存在单调增且每两个位置差不超过 \(k\) 的子序列。

\(dp_i\) 表示以位置 \(i\) 结尾的串数量。

我们发现,假如当前的值是 \(x\),我们可以找到 \([x-k,x-1]\) 中下标最大的一个,假设它的值是 \(y\),位置是 \(j\)。那么所有不能转移到 \(j\) 的,如果在 \(j\)\(i\) 之间,因为 \(j\) 是符合要求的最靠近的,那么这个位置肯定不符合答案。如果在 \(j\) 前面,那么要么是值在 \([j+1,x-1]\),要么不够转移到 \(j\),就更不能转移到 \(i\)

两种情况分开处理,先处理第一部分,\(dp_i=dp_j+1\)

然后处理 \([j+1,x-1]\),直接在线段树上查询。

那么,我们用权值线段树维护区间最大值,区间和,支持单点修改即可。

复杂度 \(O(n\log n)\)

D - Conveyor

我们发现,两个史莱姆是不可能相遇的,因为每个史莱姆的 \(x+y\) 一定等于它存在的时间长度。而每个史莱姆的存在时间长度是不相同的。

那么,我们就可以将其划分成“从时刻 \(t\) 出发的史莱姆”。而如果 \(t-x-y\) 小于 \(0\),那么就没有该时刻出发的史莱姆,直接排除即可。

如何判断时刻 \(t\) 出发的史莱姆能否经过 \((x,y)\) 呢?

这个想法非常智慧,就是记录对于 \([0,t]\) 所有时刻的史莱姆,会经过 \((x,y)\) 多少次。这个是可以计算的。从 \(f_{(0,0)}=t+1\) 开始,因为每走一次都会更换下次的方向,所以往右走和往下走次数相差始终不超过 \(1\)。而且如果有相差,一定是往右多走一次,因为初始方向是往右。当前位置会往右走 \(\lceil f_{x,y}/2\rceil\) 次,往下走 \(\lfloor f_{x,y}/2\rfloor\)

那么,我们就能递推出每个位置走了多少次。

分别对 \(t-1\)\(t\) 查询 \((x,y)\) 走了多少次,观察两次有没有变化。有,说明 \(t\) 走了,没有,说明 \(t\) 没走。

复杂度 \(O(n^2q)\),实际上跑的挺快的。

E - Minlexes

考虑从后往前贪心。

我们从 \(n\) 开始,每次往前面加入一个数,更新答案串,然后记录答案串的前 \(5\) 位和后 \(2\) 位。

发现,如果 \(T\) 是后缀 \(i+1\) 的最优解,首先后缀 \(i\) 能够转移到 \(s_iT\),其次\(s_iT\) 一定是可能的最优解转移方向。

如果 \(s_i\) 和后面不合并,最优解一定是 \(s_iS\),而 \(S\) 就是 \(i+1\) 的最优解 \(T\)

如果 \(s_i\) 和后面合并,虽然可能出现本来和 \(s_i\) 合并的被别人合并了,但是这个“别人”显然也是和 \(s_i\)\(T_0\) 是相同的,那么谁合并谁没什么意义,都是一样的。

所以,就有显然的贪心策略:每次对于当前的串 \(T\),在前面加入一个 \(s_i\),如果将 \(s_i\) 和后面合并是优的,就将 \(s_i\) 和后面合并,否则不合并。然后直接记录 \(T\) 长度和前后部分。

什么时候合并是优的呢?我们发现,将 \(s_i\)\(T_0\) 合并,当且仅当对于 \(T_0\) 后面第一个不是 \(T_0\)\(T_i\),它比 \(T_0\) 来的小。这样前面合并,它提前,字典序变小。如果 \(T\) 中只有这一个字符,那么也是合并更优,可以当作 \(T\) 的末尾有一个小于 \(a\) 的字符。

我们可以对答案串中每个位置记录 \(lst_i\) 表示下一个和当前字母不同的是谁。然后我们需要将 \(T\) 倒序存储,同时也方便我们从末尾插入。

但是这样做还有一个问题,就是 hxxh 这样的,原题要求只有在一开始相邻的才能删除,但是这里却可以先删掉 xx,然后 hh 就相邻,也可以删掉。

那么我们可以每删一对,都给下一位打上 \(tag\),标记一下这个前面已经有东西了,虽然 \(lst\) 照常继承,最后照常相邻,但是不能用后面新加入的消掉它。

这样就可以做完了,复杂度 \(O(n)\),瓶颈居然在提取前 \(5\) 位和后 \(3\) 位有十倍常数。

posted @ 2023-09-07 07:52  jucason_xu  阅读(10)  评论(0编辑  收藏  举报