题意
给定一个长为 n 的字符串 S,其每一位为数字 0∼9 或一个 . 字符,后者表示该位可以为任何数字。
再给定 m 个字符串 Ti,每个字符串有其对应的权值 Vi。
初始时有变量 Magic=1 和 Count=0。
每当 Ti 在 S 中出现一次(即不同位置出现算作多次),我们就将 Magic 乘上 Vi,并将 Count 加上 1。
最终的答案为 Answer=CountMagic。
要求找到一种填充字符串内 . 字符的方案,使得 Answer 最大,并且需要输出一种满足其的填充字符串的方案。
Data Range:1≤n,m≤1501,1≤Vi≤109,1≤i=1∑m∣Ti∣≤1501
题解
(你真的会高精度开任意次方根吗?)
所以这题显然要在答案的式子上下点功夫。
和乘积有关的题目,一个好的思路是化乘为加。我们如果把每个 Vi 都求 ln,就可以把 Magic 由求积转化为求和了,并且这样的转化并不会影响答案之间的大小关系。
而 Count 次方根这个部分,可以发现它直接变成了 Count1,所以原式就化为了 CountMagic。
我们再重新审视一遍 Magic 和 Count:每次当前者加上一个 Vi 时,后者就会加上一个 1,而又因为两者的贡献是呈分数形式的,所以我们需要用到的算法已经很明显了 —— 分数规划。
考虑分数规划的经典解法,我们二分一个答案 mid,那么有:
Counti=1∑CountVi>midi=1∑CountVi>Count∗midi=1∑Count(Vi−mid)>0
推导到这里,可以发现,只要给每一个权值减去一个 mid,那么这个式子的值和 Count 的具体值已经没有任何关系了。
剩下的部分,就是求出左侧式子的最大值,然后和 0 比较了。
而我们要如何求这个最大值呢?可以发现它和普通的分数规划不太一样:我们无法决定每个贡献的系数是 0 还是 1。
我们再次重新审视一遍这个问题:填充 S 的所有 . 字符,使得出现的所有 Ti 的权值和最大。
对于这种涉及到了字符串的多模匹配的问题,我们可以考虑用 AC 自动机来解决。具体地,设 fi,j 表示走到自动机的 i 号节点,同时 S 串已经填完了前 j 个字符,在这种情况之下我们能够得到的最大权值。
转移很简单,由于自动机具有优美的结构,因此我们仅需每次枚举下一个字符进行转移即可。注意由于我们最终需要输出一个满足要求的字符串,因此转移时需要记录前驱状态和转移时枚举的字符。
最后一个问题来了:如何处理自动机上每个点的贡献?
显然,对于一个点,如果它对应了一个 Ti,那么在 Fail 树中以它为根的子树都需要加上它的权值。所以,对于每一个 mid,我们利用树上差分重构一遍自动机上每个点的贡献即可。
代码实现
完