CodeChef COVERING - Covering Sets 题解
蒟蒻 CC 首 A 祭!写篇题解纪念一下。
虽然并没有那么难,但是我想了整整一周,今天不知道怎的英语课看电影的时候就想出来了(
先考虑只有两个数组 \(A,B\) 的情况,是要求 \(\sum\limits_iR(i)\) 的值。
如果 \(R\) 的定义是 \(i\) 能覆盖 \(j\cup k\) 的话,那就非常简单了,就直接枚举 \(i\) 然后高维前缀和相乘就可以了(这个后面说)。但实际上是 \(j\cup k\) 覆盖 \(i\),这两者本质是不同的。我们考虑先换个贡献体,用 \((j,k)\) 来贡献,那么答案就是 \(\sum\limits_i\sum\limits_jA_iB_j2^{|i\cup j|}\)。
然后这个你要是直接枚举 \(i\) 的话,那么最好也只能做到子集枚举的复杂度(以我当前水平)。我们考虑枚举 \(i\cup j\) 的值,本质上也是一次换贡献体,那么答案是 \(\sum\limits_k2^{|k|}\sum\limits_{i\cup j=k}A_iB_j\)。
那么我们只需要求出 \(\forall k,\sum\limits_{i\cup j=k}A_iB_j\)。这个可以很自然的想到这样一个思路:先看 \(\sum\limits_{i\subseteq k}\sum\limits_{j\subseteq k}A_iB_j\),这个可以把 \(A_i\) 往前移一格,然后就变成了 \(A,B\) 两个数组的高维前缀和相乘(就是前面说要后面说的那个东西)。这个式子中的 \((i,j)\) 可以保证 \(i\cup j\subseteq k\),但是不一定 \(=k\)。不难想到把 \(i\cup j\subsetneq k\) 的那些 \((i,j)\) 的贡献给删掉,不难想到这样一个 DP:\(dp_i=SumA_iSumB_i-\sum\limits_{j\subsetneq i}dp_j\)。
求这个 DP 数组的话,最直观的想法是动态维护高维前缀和,但这真的可做吗……(upd. 2020.12.25:好像真的可做,真香/cy(那这个高维差分不就可以用动态高维前缀和随便做了吗))其实这个 \(d_i=a_i-\sum\limits_{j\subsetneq i}d_j\) 这个形式是一个很经典的东西,它是高维差分的一个经典模型。高维差分这个东西本身不是很好理解,但可以用它的逆运算——高维前缀和来理解它,不难发现由 \(d_i=a_i-\sum\limits_{j\subsetneq i}d_j\) 有 \(a_i=\sum\limits_{j\subseteq i}d_j\),由后面是高维前缀和可以得到前面就是高维差分。那么就 \(\mathrm O(2^nn)\) 算一下就可以了。
然后扩展到 \(3\) 个数组的话基本没有变化,就高维前缀和相乘的时候多出来一个乘数。
吹个牛逼,之前我学高维前缀和的时候自己 yy 出了这个高维差分,感觉啥用没有,但现在看来是个挺常用的东西(
code(三分钟代码)