2019暑假正睿集训8.8day5题解及总结

题解

T1 小D与子序列

100pts

动态规划(背包)

题意:选尽量少的数字,使其总和等于给出的数m,并在此基础上使选出的最大的数和最小的数的差值最小

先对所有数字从小到大排序,因为选的是子序列而非子串,所以与数字原来所在的位置无关。

f[i]表示选出的数字和为i时取数的最小个数;

mi[i]表示选出的数字和为i时选出的最小的数

ma[j]表示选出的数字和为i时选出的最大的数

状态转移方程如下:

 for(int i=1;i<=n;i++)
    {
        for(int j=m;j>=a[i];j--)
        {
           if(f[j-a[i]]+1<f[j])
            {	 
			  f[j]=f[j-a[i]]+1;
			  if(mi[j-a[i]]==-1)mi[j]=ma[j]=a[i];
			  else{
			  mi[j]=mi[j-a[i]];
			  ma[j]=a[i];} 
            }
        }
    }

答案即为 ma[m]-mi[m]

T2 小D与字符串

LCP 找一个子串出现两次且长度最长。

70pts

r:长循环的循环次数

c[i]:代表i字母的个数

f[i]:循环节的长度 g[i]除循环节外多出来的部分的长度

f[i]*r+g[i]=c[i]

求max(f[i](r-1)+g[i]) 即max(f[i](r-1)+c[i]-f[i]*r);

当f[i]>=c[i]-f[i]*r 要f[i]尽量小

100pts

我们只考虑这两次出现组成的字符串,把其他字符都丢掉。

那么现在的限制变成 了每种字符的出现次数都不超过 ci。

我们考虑现在的字符串 S,容易发现他是一个循环串: S A B 设循环次数为 k,每个字母在单次循环中的出现次数为 ai,在最终剩余的不完整 循环中出现次数为 bi ≤ ai,则需要满足:

ai · k + bi ≤ ci 容易发现每个字母相互独立,因此我们可以对于每个 i 分别计算。

对于每个 i,我 们都要在满足上式的基础上,最大化 ai · (k − 1) + bi。

因为我们需要满足 bi ≤ ai,因此有 ai ≤ ⌊ ci k+1 ⌋,我们计算出此时的 bi = min{ai , ci− k · ai}。

因为 ai 只能减小,则我们计算若 ai 减小 1,而 bi 增加 k,其变化量为多少: (ai + 1) · (k − 1) + (bi + k) = ai · (k − 1) + bi + 1

因此若 ai 减小 1 且 bi 可以增加 k,则此时的答案会增加 1。

所以我们要进行尽 量多这样的操作,但是因为答案只能增加 k,所以如果 bi 的增加量不到 k,则一定不 优。于是我们直接计算出 t = ⌊ ai−bi k+1 ⌋,然后令 a ′ i = ai − t,b ′ i = bi + kt 即可。

根据刚 刚的分析,这就是一组最优解。 于是我们如果枚举 k,则时间复杂度为 Θ(∑n i=1 ci)。

但是注意到 ai 的初始值为一个整除的形式,因此只有 Θ(√ ci) 种。而对于一个相 同的初始 ai,不难发现 k 越大越好。因此可能作为最优解 k 一共只有 Θ(n √ ci) 种, 故我们取出所有这样的 k 然后进行刚刚的算法即可。

T3 小D与计算

100pts

任务 1 :问题等价于于取反后的最低位,可以利用溢出:先左移 63 位再右移 63 位即可, 取反和赋值合起来即可做到 3 次操作。

任务 2 :先求出不进位加法的结果,即 a ⊕ b;然后考虑进位 (a ∧ b) ≪ 1。 则我们的问题又变成了两个数字的加法,而后者的最低位每次至少会增加 1,因此这一过程只会进行最多 64 次。注意需要根据奇偶性决定当前是哪两个位置相加。

任务 3:可以发现这就是上面两个任务拼起来,直接做即可。 注意这里加法的运算次数需要根据答案大小进行调整。

任务 4:相当于需要实现三目运算符:某个寄存器的取值在 ai 为 1 时取 aj,否则取 ak。 我们注意到这里根本没法做 if 或者加法或者 max,不过我们可以做 ∨:我们把 不取的那个变成 0,取得那个不动,然后 ∨ 起来即可。 考虑怎么实现不动或者变成 0,可以发现这两者分别相当于与全 1 或者全 0 进行 ∧。于是我们相当于要把 ai 这个 bit 填满整个寄存器。 直接挪动开销过大:需要 64 次操作,不可接受。不过我们可以考虑倍增:我们进 行 i 次(0 ≤ i < 6),每次我们令 x = x ∨ (x ≪ 2 i ) 即可。

任务 5 我们相当于要比较两个数字的最高位哪个大,设为 a, b。 我们考虑类似上面那个子任务中那样倍增,即可使得 a 的最高位以下全都变成 1。 然后我们将其取反后与 b 进行 ∧ 操作,则如果 b 的最高位比较高,这边就会得 到非 0 的数字。我们再倍增把它推下去和推上来即可得到全 1;而如果 a 的最高位比 较高,那么得到 0,无论如何都是 0 了。

任务 6 因为无法使用 if 这样的流程控制语句,考虑使用冒泡排序:利用任务 4 做出比 较器,并利用任务 5 做出三目运算符决定某个位置的值,然后两个异或一下即可

今日总结

1.这次集训第一次A一道题,太感动了(虽然是第一题orz)。由于吸取了昨天DP爆零的教训,今天写第一道题的时候搞了几组数据,把过程都输出了,手推了一遍,没有什么太大问题后才交上去。之前一直写DP经常写挂,可能因为它比较玄学,但其实输出过程之后,感觉还是很有道理的。

2.第二题打了一下暴力,得到了的10分暴力分,没有太大的遗憾(要是能想出正解就更好了)。本以为是不是特别懂的的字符串里的sam,后缀自动机什么的,结果没想到最终是智商题。

3.虽然很多题都不一定想得出正解,希望能把自己会的暴力尽量打对,争取能拿一分是一分。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值