zoukankan      html  css  js  c++  java
  • 【Wannafly挑战赛29F】最后之作(Trie树,动态规划,斜率优化)

    【Wannafly挑战赛29F】最后之作(Trie树,动态规划,斜率优化)

    题面

    牛客

    题解

    首先考虑怎么计算([l,r])这个子串的不同的串的个数。
    如果(l=1),我们构建(Trie)树然后第(i)层的点的个数就是([1,i])的答案。
    如果(l)要向右移动一位,显然就是我们要把最上面那一层给删掉。
    那么我们暴力对(Trie)树进行合并,因为每个点最多只会被合并一次,所以复杂度是(O(|sum|*tot))的,其中(tot)(Trie)树点数,(|sum|)是字符集大小。
    因为数据范围较大,显然不可能暴力把所有([l,r])的答案都维护出来,但是我们发现当(l)固定的时候,(r)单增时,([l,r])不同的串的个数也是单增的,所以我们只需要维护一下单增的位置就行了。
    考虑维护数组(g[i][j])表示当前左端点在(i),不同的串的数量为(j)时最靠左的右端点。这个每次二分一下就行了。
    接下来就把(g[i][j])设为([i,j])这段区间的不同的串的个数了,这个只需要在我们预处理的东西里二分。
    如果(dp)的话,显然大家都会(O(len^2))的暴力。
    因为上面预处理是右侧的第一个右端点,所以我们考虑从后往前(dp)。如果要从前往后(dp)的话就维护以这个点为右端点的第一个左端点。这样子的话串要反着插入进(Trie)中。
    (f[i])表示当前已经考虑完了([i,len])的最大贡献,显然(i)是左端点,我们就需要枚举一个右端点,此时的转移是:

    [f[i]=max_{r=i}^{len}{f[r+1]+p*g[i][r]+a[i]*(r-l+1)+b[i]} ]

    我们对于(g[i][r])相同的一段(r)可以一起考虑,这样子要求的本质上就是(val[r]+a[i]*r)的最大值,其中(val[r])是只和(r)相关的东西。发现这个东西是一个一次函数的形式,要求在(x=a[i])位置时的最大值。不难想到这是一个斜率优化的形式,于是就可以拿李超线段树直接维护了,这样子的复杂度是(O(n*len*logn))

    首先注意到因为(p>0),所以即使我们可以直接把(g[i][r])更大的(r)也给当成当前的(g[i][r]),也就是我们每次转移都是从一段后缀转移过来。
    于是我们的问题可以变成从后往前依次把点加入进凸壳,在每个时刻凸壳加入完之后,我们要把它给转移到若干位置上去。这样子发现逃脱不了在凸壳上二分的问题。也就是我们并不能优化复杂度。因为(a_i)不具有单调性。
    那么,现在我们把(dp)正过来做,变成:

    [f[i]=max_{l=1}^{i}{f[l-1]+p*g[l][i]+a[l]*(i-l+1)+b[l]} ]

    于是要求的东西变成了(val[l]+i*a[l])最大。
    我们要构建的凸壳也变成了前缀凸壳,每次查询的(x=i)
    这个时候发现查询的东西具有了单调性,那么我们就可以有更加有趣的做法了。
    考虑对于(kin[1,n])维护一个在凸壳上的指针,维护的表示的是(g[l][i]=k)时,在凸壳上查询的指针。每次修改凸壳的时候,如果当前指向的位置为删掉了,显然指针就会减一,然后再暴力向右移动就好了。
    因为凸壳的点不单调,所以要用平衡树维护凸壳,简单的用(set)实现就好了。
    这样子的复杂度是(O(nm+nlogn))

    代码它咕咕咕了。

  • 相关阅读:
    通过Javascript调用微软认知服务情感检测接口的两种实现方式
    Microsoft Flow 概览
    使用PowerApps快速构建基于主题的轻业务应用 —— 进阶篇
    从三个语言(C++,Java,C#)的几个性能测试案例来看性能优化
    自己动手,打造轻量级VSCode/C#环境代替LinqPad
    2015年总结
    将知识变成你的技能点
    瞎子摸象与刻舟求剑
    俺的追求,下一个五年的指导纲领
    工作中任务管理的四个原则和四个技能
  • 原文地址:https://www.cnblogs.com/cjyyb/p/11120792.html
Copyright © 2011-2022 走看看