CF653G

一道埃筛 + 组合数学的题目。

题意:给定一个序列 {a},执行以下的操作直到所有数相等:乘一个质数或除一个质数。求所有子序列最少操作次数的和,对 109+7 取模。n,ai3×105,5 s。可以加强到 n,ai2×107

容易发现每一个质数是独立的,可以分开计算贡献。另外,显然当取质数次数的中位数是最优的策略。

对每一个质数分别考虑,假设当前按质数次数降序排名为 i 的数次数为 xi,中位数为 x,那么答案为 |xix|

如果我们将整个展开,发现 n2 个数 x 的贡献是负,n2 个数 x 的贡献是正,那么我们只需要考虑 xi 的贡献。

假设排名小于 i 的选了 a 个(0a<i),大于 i 的选了 b 个(0b<ni+1),那么根据 ab 的大小可以分为:

  1. a<b,则 xi 贡献为正。
  2. a=b,则 xi 贡献为 0。
  3. a>b,则 xi 贡献为负。

a,b 的相对关系看起来不好枚举,我们换做 i1a+bi1 的大小关系可能更好枚举。于是我们可以得到下面的式子:

j=in1(n1j)j=0i2(n1j)

我们可以预处理 f(k)=i=kn1(n1i)i=0k2(n1i),可能会更好求。

发现 xi 一样的段,贡献是连续的,也就是 f(k) 是一段的和。假设枚举次幂 t,设 xit 的个数为 axi>t 的个数为 b,那么 t 的贡献就是:

ti=b+1af(i)

再次前缀和 g(i)=j=1if(j),那么这个贡献也可以 O(1) 计算。那么瓶颈在于计算 xit 的个数。这个可以通过埃筛来计算。

具体来说,我们直接考虑枚举质数 p,然后对于他的次幂,暴力计算次幂的倍数有多少个。据说时间复杂度是 O(nloglogn) 的,实测 4×107 也只要 0.5s(不算读入)。有好心人发一下证明吗?

代码比较简短。放一个主函数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
pre[0] = 1;
for (int j = 1; j < n; ++ j) adj(pre[j] = pre[j - 1] + C(n - 1, j) - Mod);
for (int i = 1, tmp; i <= n; ++ i)
adj(ans[i] = ans[i - 1] + adj(adj(tmp = pre[n - 1] - pre[i - 1]) -= (i == 1 ? 0 : pre[i - 2])) - Mod);

int res = 0;
for (int i = 2, j; i <= mx; ++ i) {
if (st[i]) continue;
for (j = i << 1; j <= mx; j += i) st[j] = true;
LL k = i;
for (cntp[j = 1] = 0; k <= mx; cntp[++ j] = 0, k *= i)
for (int l = k; l <= mx; l += k) cntp[j] += cnt[l];
for (int l = j - 1, tmp; l; -- l)
res = (res + (LL)adj(tmp = ans[cntp[l]] - ans[cntp[l + 1]]) * l) % Mod;
}

Gitalking ...