ARC184B 题解

blog。算是单杀 * 2800 了(虽然做过 [HNOI2012] 集合选数)。来写一个题解区复杂度最劣做法。

思路

考虑很多元素是可以分开处理的。例如 \(\{1,2,3,4,6,9,\cdots\}\) 需要一起处理,而 \(5,10\) 等数并不会影响到这个集合。

对于每一个集合,考虑构造如下矩阵:

\[\begin{matrix}x & 3x & 9x & 27x & \cdots\\2x & 6x & 18x & 54x & \cdots\\4x & 12x & 36x & 108x & \cdots\\8x & 24x & 72x & 216x & \cdots\\\vdots & \vdots & \vdots & \vdots\end{matrix} \]

就是 \(a_{x,y}=2a_{x-1,y}=3a_{x,y-1}\),元素超出 \(n\) 就停止(所以这个实际是楼梯状物)。

那么考虑转换后的问题:选择一个 \((x,y)\),覆盖 \((x,y)(x+1,y)(x,y+1)\),求最少几次可以覆盖全部位置

并不好做。但是这个东西的大小只有 \(\log_2 10^9 \times \log_3 10^9=30\times19\),所以可以暴力状压啦!

\(dp_{i,s}\) 表示前 \(i\) 行,当前行的选择状态为 \(s\),最小次数。转移容易:

\[dp_{i,s}=\min(dp_{i-1,t})+1 \qquad \Large(\normalsize t\cup s\cup (s\texttt{ << }1)\Large)\normalsize\text{ cover this line} \]

for (int s = 0, U = (1 << len[1]) - 1; s <= U; s++)
    if (((s | (s << 1)) & U) == U) dp[1][s] = __builtin_popcount(s); else dp[1][s] = 1e9;

for (int i = 2; i <= 30; i++)
    for (int s = 0, U = (1 << len[i]) - 1; s <= U; s++) {
        int qwq = 1e9;
        for (int t = 0; t < (1 << len[i - 1]); t++)
            if (((t | s | (s << 1)) & U) == U) qwq = min(qwq, dp[i & 1 ^ 1][t]);
        dp[i & 1][s] = qwq + __builtin_popcount(s);
    }

int ans = 2e9;
for (int s = 0; s < (1 << len[30]); s++) ans = min(ans, dp[0][s]);
return ans;

其中 \(len_i\) 表示第 \(i\) 行需要覆盖的长度。答案是所有集合贡献的和。

优化

呃这个代码 8s 只能跑 3e7,非常菜。考虑找小性质:

C1:矩阵开头的数为 \(6k+1\)\(6k+5\)。原因显然。(其实就是无法整除 \(2,3\) 的数。)

C2:对于 \(a_{1,1}=x\) 且最大值为 \(n\) 的矩阵,等价于 \(a_{1,1}=1\) 且最大值为 \(\dfrac nx\) 的矩阵

所以,记 \(\operatorname{sol}(n)\) 表示 \(a_{1,1}=1\) 且最大值为 \(n\) 的矩阵的最小值,答案即为

\[\sum\limits_{i=6k+1\text{ 或 }6k+5}\operatorname{sol}(\dfrac ni) \]

这玩意可以很快枚举,但是求个 \(\operatorname{sol}(10^9)\) 就要很久了,更别说其他的了啊???

C3:本质不同的矩阵只有极少个。证明:最多只会有 \(30\times 19\) 个元素,而随着 \(n\) 变小,数量只会变小,因此本质不同的矩阵不超过 \(570\) 个。实际只有 \(300\) 个左右。

依据这个,可以对矩阵的 \(len_i\)哈希起来,然后打表直接获取所有可能的 hash 值对应的答案。

打表跑了一个小时。实现精细点其实可以快很多的 qwq(


code。里面包含了打表程序与最终程序。

最终程序为 \(O(n)\)(或 \(O(\sqrt n)\) 整除分块)。

posted @ 2024-09-25 22:08  liangbowen  阅读(47)  评论(0)    收藏  举报