CF258C Little Elephant and LCM
\(\verb!upd:!\) 之前逻辑有点错误,修改一下。
花了 ** 三天又\(4\) 个小时在这道题上才理解。。。
修改是半个月之后。
题目大意
对于一个数组 \(a\),有数组 \(b\) 使得 \(b_i\le a_i\) 并且 \(b\) 中所有数的最小公倍数等于 \(b\) 中所有数字的最大值,求这样的数组 \(b\) 的个数 \(\bmod 10^9+7\).
题目分析
因为 \(b\) 数组的最大值等于 \(\operatorname{lcm}\{b_i\}\),所以 \(b\) 数组所有数一定为 \(\max\{b_i\}\) 的因子。又因为 \(\forall i,b_i\le a_i\),所以可以枚举 \(b\) 数组的最大数,范围从 \(1\) 到 \(\max\{a_i\}\)。
因为 \(a\) 数组的顺序不会对答案有影响,所以我们将 \(a\) 数组升序排列,对于枚举的 \(i=\max\{b\}\),遍历 \(i\) 所有因子,从小到大记为 \(divi_0,divi_1,\cdots,divi_{m-1}\),通过二分找出 \(a\) 序列中小于 \(divi_j,j\in [0,m-1]\) 的位置 \(at\),计算贡献即可。
枚举间隙 \([divi_0,divi_1),[divi_1,divi_2),\cdots,[divi_{m-3},divi_{m-2}),[divi_{m-2},divi_{m-1}),[divi_{m-1},\max\{a_i\}]\)。
对于前面的一堆左闭右开区间 \([divi_j,divi_{j+1}),j\in [0,m-2]\),单看一个的贡献为 \(num\) 个 \(j+1\) 的积(\(j+1\) 即 \(divi_0,divi_1,\cdots,divi_j,divi_{j+1}\) 这些可能的因数个数),即 \((j+1)^{num}\)(\(num\) 表示区间内有多少个 \(a\) 序列中的数),如果不是很清楚下面会解释。对于最后一个闭区间,区间内每个数都有 \(m\) 种取值,但我们必须有一个数取到了 \(\max\{a_i\}\),此时贡献为 \(m^{num}-(m-1)^{num}\)。
举个例子:1 2 3 4
。枚举最大值,比如说是 \(4\),\(a\) 序列中包含 \(4\) 的因子为 \(1,2,4\),此时方案数就为 \(1\times 2\times 2\times (4-3)=4\)。
可以利用 vector
\(\mathcal{O(max_{a_i}\log max_{a_i})}\) 预处理出 \(1\sim max_{a_i}\) 的因数。幂运算快速幂就够了。
总时间复杂度为 \(\mathcal{O(n\log^2n)}\)。
代码
//2022/3/11
//2022/3/12
//2022/4/3
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <cstdio>
#include <climits>//need "INT_MAX","INT_MIN"
#include <cstring>//need "memset"
#include <numeric>
#include <algorithm>
#include <vector>
#define int long long
#define enter() putchar(10)
#define debug(c,que) cerr << #c << " = " << c << que
#define cek(c) puts(c)
#define blow(arr,st,ed,w) for(register int i = (st);i <= (ed); ++ i) cout << arr[i] << w;
#define speed_up() cin.tie(0),cout.tie(0)
#define mst(a,k) memset(a,k,sizeof(a))
#define Abs(x) ((x) > 0 ? (x) : -(x))
const int mod = 1e9 + 7;
inline int MOD(int x) {
if (x < 0) x += mod;
return x % mod;
}
namespace Newstd {
char buf[1 << 21],*p1 = buf,*p2 = buf;
inline int getc() {
return p1 == p2 && (p2 = (p1 = buf) + fread(buf,1,1 << 21,stdin),p1 == p2) ? EOF : *p1 ++;
}
inline int read() {
int ret = 0,f = 0;char ch = getc();
while (!isdigit(ch)) {
if(ch == '-') f = 1;
ch = getc();
}
while (isdigit(ch)) {
ret = (ret << 3) + (ret << 1) + ch - 48;
ch = getc();
}
return f ? -ret : ret;
}
inline void write(int x) {
if(x < 0) {
putchar('-');
x = -x;
}
if(x > 9) write(x / 10);
putchar(x % 10 + '0');
}
}
using namespace Newstd;
using namespace std;
const int ma = 1e5 + 5;
int a[ma];
vector<int>divi[ma];
int n;
inline void init() {
for (register int i = 1;i <= 1e5; ++ i) {
for (register int j = i;j <= 1e5;j += i) {
divi[j].push_back(i);
}
}
for (register int i = 1;i <= 1e5; ++ i) sort(divi[i].begin(),divi[i].end());
}
inline int ksm(int n,int m,int p) {
int res = 1;
for(;m;m >>= 1ll) {
if (m & 1ll) res = res * n % p;
n = n * n % p;
}
return res % p;
}
#undef int
int main(void) {
#ifndef ONLINE_JUDGE
freopen("in.txt","r",stdin);
#endif
#define int long long
init();
n = read();
for (register int i = 1;i <= n; ++ i) a[i] = read();
sort(a + 1,a + n + 1);
int ans = 0;
for (register int i = 1;i <= a[n]; ++ i) {
int prod = 1,cnt = divi[i].size();
for (register int j = 0;j < cnt - 1; ++ j) {
int num = lower_bound(a + 1,a + n + 1,divi[i][j + 1]) - lower_bound(a + 1,a + n + 1,divi[i][j]);
prod = MOD(prod * ksm(j + 1,num,mod));
}
int num = lower_bound(a + 1,a + n + 1,a[n] + 1) - lower_bound(a + 1,a + n + 1,divi[i][cnt - 1]);
prod = MOD(prod * MOD(ksm(cnt,num,mod) - ksm(cnt - 1,num,mod)));
ans = MOD(ans + prod);
}
printf("%lld\n",ans);
return 0;
}