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;
}
posted @ 2022-03-12 11:40  Coros_Trusds  阅读(73)  评论(0编辑  收藏  举报