题意:

有一个长度为\(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. 使用SpringSession管理分布式系统的会话Session

    在我方供应链项目分布式部署的环境下,需要在统一网关服务中管理访问的Session,即无论访问请求路由到哪一个网关服务环境,使用的都是相同的HttpSession,这样就保证了在用户登录之后,能够使用统 ...

  2. hibernate课程 初探单表映射2-1 hibernate进阶 本章简介

    本章简介,主要讲5大块的内容 1 hibernate.cfg.xml的配置 2 session 的简介 3 transaction的简介 4 session的详解 5 对象关系映射常用配置

  3. windows无法连接到打印机 操作失败,错误为0x00000002 解决方案

    平时使用局域网打印机没有问题,今天突然脱机了,错误号为0x00000002 服务器上打印机一切正常,别人使用也一切正常. 最后,重启了Spooler服务后搞定. 重新链接打印机,搞定!

  4. Eucalyptus常用查询命令

    前言: Elastic Utility Computing Architecture for Linking Your Programs To Useful Systems (Eucalyptus)  ...

  5. 【Android开发笔记】生命周期研究

    启动 onCreate onStart onResume 退出键 onPause onStop onDestroy 锁屏 & 按住 home键 & 被其他Activity覆盖(Sing ...

  6. thinkphp 3.2.3版本学习笔记

    2.开启调试模式,有什么作用?(默认关闭,在ThinkPHP.php 33行左右) (1)非法调用的时候,有详细的报错信息,便于调试 (2)APP_DEBUG为true并且缓存文件存在,走缓存文件,否 ...

  7. pat乙级1067

    1.用cin输入数据后,再用getline 输入,还是会输入cin已经输入的数据,即cin和getline互相独立. 2.题目中没有说尝试的密码不包含空格,因此不能用cin,而用getline. #i ...

  8. Producer & Consumer

    需要与Eureka结合使用 Eureka环境搭建 Producer 一.pom文件 <?xml version="1.0" encoding="UTF-8" ...

  9. Android(java)学习笔记89:Bundle和Intent类使用和交互

    1. Bundle 和 Intent:    Bundle只是一个信息的载体 将内部的内容以键值对组织 ,Intent负责Activity之间的交互自己是带有一个Bundle的.Intent.putE ...

  10. Java AES加密算法工具类

    AESCodec.java package util; import java.security.Key; import javax.crypto.Cipher; import javax.crypt ...