像潮落潮涌,送我奔向自由。|

寂静的海底

园龄:3年2个月粉丝:58关注:15

【题解】CF1856E2 背包 复杂度分析 压位

此处假设读者已经通过 E1,会 O(n2) 地解决本问题:

在每一个点时做的操作就是“把儿子大小构成的数集合分成差尽可能小的两部分”。

接下来来解决 E2:

首先一个观察就是单个问题“把儿子大小构成的数集合分成差尽可能小的两部分”是一个 sizin 的背包可行性问题。没有 O(nlogn) 的做法,常见的做法有三种:

  • sizin 所以 sizi 的种类只有 n 种,直接做余数分类 O(nn)
  • 将每种 sizi 的个数进行二进制拆分,然后 bitset 压位复杂度 O(nnlognw),常数很小。
  • 多项式卷积,NTT 或 FFT 合并,O(nlog2n),常数大。

令上方解决规模 n 单层问题的复杂度为 C(n)

如果每层都解决一次这样的问题,复杂度仍然是 C(sizi) 的,这最坏仍然可以达到 O(n2)

因为树 siz 的构成是具有特殊性的,解决完这个问题后子问题的规模和这个问题的构成是密切相关的 (这显然弱于解决任意 n 次上面问题)。

又因为我们只是想尽可能平均分两个集合,所以当重儿子的 siz 大于其它儿子的 siz 之和时,一定会选择将重儿子放在一个集合中,所有轻儿子放在另一集合,所以只有重儿子的 siz 小于目前 siz 的一半时我们才会进行背包,而如果重儿子的 siz 小于目前 siz 的一半,然后所有子问题的规模都变成了原来的一半,所以最坏复杂度为 T(n)=2T(n/2)+C(n)

带入具体复杂度分析都是简单的主定理:

如果 C(n)=O(nn)T(n)=2T(n/2)+O(nn)=O(nn)

如果 C(n)=O(nnlognw)T(n)=2T(n/2)+O(nnlognw)=O(nnlognw)

如果 C(n)=O(nlog2n)T(n)=2T(n/2)+O(nlog2n)=O(nlog3n)

第三种理论最优,但是在题目数据范围下使用第二种,最坏耗时约 300ms,可以轻松通过。

一个实现上的小问题是需要使用动态大小的 bitset,可以选择手写或者提前开好所有大小为 2kbitset,然后使用的时候选择最小的可用的。

这里是代码

upd:可以证明更优秀的复杂度:直接进行二进制拆分的复杂度其实是 C(n)=T(n)=O(nnw) 的,具体的复杂度分析见 这里 或者 这里,感谢 @王熙文 的指出,Orz。

posted @   寂静的海底  阅读(8)  评论(0编辑  收藏  举报  
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起