题意:

有一个长度为\(n\)的正整数序列\(a\),有这样一种操作:

每次可以选序列中的某一个数乘上或除以某一个素数。

求对于每一个子序列使其所有元素相等的最少操作次数之和。

分析:

因为两个素数之间互不影响,单独考虑每一个素数\(p\)。

设当前子序列的长度为\(k\),对应的指数为\(e_1, e_2 \cdots e_k\)。

每次操作会将某一个\(e_i\)增加\(1\)或减少\(1\)。

将\(e_i\)对应到数轴上的点,每次操作就相当于让某个点向左或向右移动一个单位长度。

让它们都相等的最少操作次数就等于这些点到中位数的距离之和\(d\)。

  • \(k=4\)时,\(d=e_4+e_3-e_2-e_1\),中位数为\(e_2\)或\(e_3\)。
  • \(k=5\)时,\(d=e_5+e_4-e_2-e_1\),中位数为\(e_3\)。

下面考虑所有的子序列:

对于\(e_k\),如果它在一个子序列的左半部分那么它对答案的贡献是\(-1\) ,如果它在序列的右半部分那么它对答案的贡献是\(+1\)。

可以在\(e_1, e_2 \cdots e_{k-1}\)中选若干数,以及在\(e_{k+1} \cdots e_n\)选若干数,构成包含\(e_k\)的子序列。

考虑下面这个生成函数:

\[(1+\frac{1}{x})^{k-1}+(1+x)^{n-k}=\frac{(1+x)^{n-1}}{x^{k-1}}
\]

  • \(e_k\)在左半部分的子序列的个数为指数为负的系数之和
  • \(e_k\)在右半部分的子序列的个数为指数为正的系数之和

因此\(e_k\)对所有子序列的贡献为:

\[\sum\limits_{i=k}^{n-1}C_{n-1}^i-\sum\limits_{i=0}^{k-2}C_{n-1}^i
\]

\[=\sum\limits_{i=0}^{n-1}C_{n-1}^i-\sum\limits_{i=0}^{k-1}C_{n-1}^i-\sum\limits_{i=0}^{k-2}C_{n-1}^i
\]

\[=2^{n-1}-2\sum\limits_{i=0}^{k-2}C_{n-1}^i-C_{n-1}^{k-1}
\]

\[=2^{n-1}-\sum\limits_{i=0}^{k-1}C_n^i
\]

而且\({e_i}\)最大不会超过\(20\),所以统计一下每个\(e_i\)出现的次数,再预处理一下组合数的前缀和的前缀和。

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std; typedef long long LL;
const LL MOD = 1000000007; LL mul(LL a, LL b) { return a * b % MOD; } void add(LL& a, LL b) { a += b; if(a >= MOD) a -= MOD; } void sub(LL& a, LL b) { a -= b; if(a < 0) a += MOD; } LL pow_mod(LL a, int p) {
LL ans = 1;
while(p) {
if(p & 1) ans = mul(ans, a);
a = mul(a, a);
p >>= 1;
}
return ans;
} const int maxn = 300000 + 10;
const int maxp = 26000; bool vis[maxn];
int prime[maxp], pid[maxn], pcnt; void preprocess() {
for(int i = 2; i < maxn; i++) {
if(!vis[i]) { prime[pcnt] = i; pid[i] = pcnt++; }
for(int j = 0; j < pcnt; j++) {
if(i * prime[j] >= maxn) break;
vis[i * prime[j]] = true;
if(i % prime[j] == 0) break;
}
}
} int n, a[maxn];
int cnt[maxp][20]; LL fac[maxn], inv[maxn], Cn[maxn]; void decompose(int x) {
for(int i = 0; x > 1; i++) {
int p = prime[i];
if(p * p > x) break;
if(x % p != 0) continue;
int e = 0;
while(x % p == 0) { x /= p; e++; }
cnt[i][e]++;
}
if(x > 1) cnt[pid[x]][1]++;
} int main()
{
preprocess(); scanf("%d", &n);
for(int i = 1; i <= n; i++) scanf("%d", a + i); fac[0] = 1;
for(int i = 1; i <= n; i++) fac[i] = mul(fac[i - 1], i);
inv[n] = pow_mod(fac[n], MOD - 2);
for(int i = n - 1; i >= 0; i--) inv[i] = mul(inv[i + 1], i + 1);
for(int i = 0; i <= n; i++) Cn[i] = mul(mul(fac[n], inv[i]), inv[n - i]);
for(int i = 1; i <= n; i++) add(Cn[i], Cn[i - 1]);
for(int i = 1; i <= n; i++) add(Cn[i], Cn[i - 1]); for(int i = 1; i <= n; i++) decompose(a[i]); LL ans = 0;
LL S = pow_mod(2, n - 1);
for(int i = 0; i < pcnt; i++) {
int tot = n;
for(int j = 1; j < 20; j++) tot -= cnt[i][j];
for(int j = 1; j < 20; j++) if(cnt[i][j]) {
int L = tot, R = L + cnt[i][j] - 1;
tot += cnt[i][j];
LL t = Cn[R];
if(L) sub(t, Cn[L - 1]);
sub(t, mul(S, cnt[i][j]));
add(ans, mul(t, j));
}
} printf("%lld\n", ans); return 0;
}

Codeforces 653G Move by Prime 组合数学的更多相关文章

  1. Codeforces 385C Bear and Prime Numbers

    题目链接:Codeforces 385C Bear and Prime Numbers 这题告诉我仅仅有询问没有更新通常是不用线段树的.或者说还有比线段树更简单的方法. 用一个sum数组记录前n项和, ...

  2. 【CF653G】Move by Prime 组合数

    [CF653G]Move by Prime 题意:给你一个长度为n的数列$a_i$,你可以进行任意次操作:将其中一个数乘上或者除以一个质数.使得最终所有数相同,并使得操作数尽可能小.现在我们想要知道$ ...

  3. Codeforces 385C Bear and Prime Numbers(素数预处理)

    Codeforces 385C Bear and Prime Numbers 其实不是多值得记录的一道题,通过快速打素数表,再做前缀和的预处理,使查询的复杂度变为O(1). 但是,我在统计数组中元素出 ...

  4. codeforces 385C Bear and Prime Numbers 预处理DP

    题目链接:http://codeforces.com/problemset/problem/385/C 题目大意:给定n个数与m个询问区间,问每个询问区间中的所有素数在这n个数中被能整除的次数之和 解 ...

  5. codeforces 932E Team Work(组合数学、dp)

    codeforces 932E Team Work 题意 给定 \(n(1e9)\).\(k(5000)\).求 \(\Sigma_{x=1}^{n}C_n^xx^k\). 题解 解法一 官方题解 的 ...

  6. Codeforces 840C 题解(DP+组合数学)

    题面 传送门:http://codeforces.com/problemset/problem/840/C C. On the Bench time limit per test2 seconds m ...

  7. codeforces 659 G. Fence Divercity 组合数学 dp

    http://codeforces.com/problemset/problem/659/G 思路: f(i,0/1,0/1) 表示到了第i个,要被切的块开始了没有,结束了没有的状态的方案数 递推看代 ...

  8. codeforces 679A Bear and Prime 100 交互

    第一次交互题,记录一下吧 #include <cstdio> #include <iostream> #include <ctime> #include <v ...

  9. CodeForces 385C Bear and Prime Numbers 素数打表

    第一眼看这道题目的时候觉得可能会很难也看不太懂,但是看了给出的Hint之后思路就十分清晰了 Consider the first sample. Overall, the first sample h ...

随机推荐

  1. SpringMVC 返回自定义属性名

    SpringMVC 返回的属性名默认是小写驼峰形式的实体对象中的属性名,如 userID 属性名它会返回 userId. 如果接口方式之前已经定下来,这样前端按原来的方式取数据会读取失败的,那有没有方 ...

  2. @Enable*注解的工作原理

    @EnableAspectJAutoProxy @EnableAsync @EnableScheduling @EnableWebMv @EnableConfigurationProperties @ ...

  3. 红象云腾CRH 一键部署大数据平台

    平台: arm 类型: ARM 模板 软件包: azkaban hadoop 2.6 hbase hive kafka spark zeppelin azkaban basic software bi ...

  4. 时序js插件cubism使用

    document http://iwantmyreal.name/blog/2012/09/16/visualising-conair-data-with-cubism-dot-js https:// ...

  5. Mysql数据库插入时乱码问题解决

    我们在利用cmd的黑屏界面进行mysql数据的插入时往往会出现不能插入的情况,这个原因是因为我们系统虽然和服务器端即mysql的数据库采用的都是统一的utf8的编码,但是在传输的过程中会变成iso88 ...

  6. hihoCoder hiho一下 第一周 #1032 : 最长回文子串 (Manacher)

    题意:给一个字符串,求最长回文子串的长度. 思路: (1)暴力穷举.O(n^3) -----绝对不行. 穷举所有可能的出现子串O(n^2),再判断是否回文O(n).就是O(n*n*n)了. (2)记录 ...

  7. linux 命令——14 head (转)

    head 与 tail 就像它的名字一样的浅显易懂,它是用来显示开头或结尾某个数量的文字区块,head 用来显示档案的开头至标准输出中,而 tail 想当然尔就是看档案的结尾. 1.命令格式: hea ...

  8. 又一次摔MFC坑里了

    因为公司的个项目最近开始写MFC了,又遇到一个坑爹的问题,使用的View视图模式在VS2010中创建的工程,默认就带入了许多的Style,例如Office 2007的许多漂亮样式确实很方便,但是同样也 ...

  9. IOS CALayer基本使用 (图层)

    ● 其实UIView之所以能显示在屏幕上,完全是因为它内部的一个图层(CALayer) ● 在创建UIView对象时,UIView内部会自动创建一个图层(即CALayer对象),通过UIView 的l ...

  10. 自建ssr(谷歌云免费试用一年)

    近期我一个朋友的VPN到期了,他也不想再去续费,同时发现谷歌云第一年申请时是免费的,所以他就自己搭建了一个自己专属的VPN 以下是他的搭建教程:  本教程难点在于申请免费试用资格 谷歌云+ssr搭建免 ...