【DP/数学】【CF1061C】 Multiplicity

Description

给定一个序列 a,求有多少非空序列 b 满足 ba 的子序列并且  k  [1,lenb],  kbk,其中 lenbb 的长度。答案对 1e9+7 取模

Input

第一行是序列 a 的长度 n

下面一行 n 个整数,代表序列 a

Output

输出一行一个整数代表答案

Hint

1  n  100000 , 1  ai  106

Solution

总觉得这题是假的= =

考虑DP。我们设 fi,j 为考虑 a 中前 i 个数,填充到 bj 个的方案数

方程显然:

j  ai 时:

fi,j = fi1,j

否则:

fi,j = fi1j + fi1j1

考虑这个方程的状态是 O(n max(ai)) 的,显然过不去,于是考虑优化:

我们发现能取到第二条转移方程当且仅当 jai 的因数,而第一种第一种转移方程可以使用滚动数组直接省略掉。于是我们直接枚举 ai 的因数,只在因数位置进行转移。同时注意因为 i 相同时的 f 时不能互相影响,所以对因数的转移要从大到小进行

考虑这么做的复杂度:

一共有 n 个数,每个数的因数是 O(ai),同时排序需要 O(ai × log(ai)),总复杂度 O(n ai log(ai))

看起来根本过不去有木有= =

但是考虑因数个数事实上是一个很松的上界,经过实际测试,[1,106] 范围内因数个数最多的数的因数不过 240 个,测试结果如下:

qwq

于是本题的实际复杂度为 Θ(n × d(ai) log(d(ai))),其中 d(ai)  240

于是就可以轻松通过本题辣

Code

#include <cmath>
#include <cstdio>
#include <vector>
#include <algorithm>
#ifdef ONLINE_JUDGE
#define freopen(a, b, c)
#endif
#define rg register
#define ci const int
#define cl const long long

typedef long long int ll;

namespace IPT {
	const int L = 1000000;
	char buf[L], *front=buf, *end=buf;
	char GetChar() {
		if (front == end) {
			end = buf + fread(front = buf, 1, L, stdin);
			if (front == end) return -1;
		}
		return *(front++);
	}
}

template <typename T>
inline void qr(T &x) {
	rg char ch = IPT::GetChar(), lst = ' ';
	while ((ch > '9') || (ch < '0')) lst = ch, ch=IPT::GetChar();
	while ((ch >= '0') && (ch <= '9')) x = (x << 1) + (x << 3) + (ch ^ 48), ch = IPT::GetChar();
	if (lst == '-') x = -x;
}

template <typename T>
inline void ReadDb(T &x) {
	rg char ch = IPT::GetChar(), lst = ' ';
	while ((ch > '9') || (ch < '0')) lst = ch, ch = IPT::GetChar();
	while ((ch >= '0') && (ch <= '9')) x = x * 10 + (ch ^ 48), ch = IPT::GetChar();
	if (ch == '.') {
		ch = IPT::GetChar();
		double base = 1;
		while ((ch >= '0') && (ch <= '9')) x += (ch ^ 48) * ((base *= 0.1)), ch = IPT::GetChar();
	}
	if (lst == '-') x = -x;
}

namespace OPT {
	char buf[120];
}

template <typename T>
inline void qw(T x, const char aft, const bool pt) {
	if (x < 0) {x = -x, putchar('-');}
	rg int top=0;
	do {OPT::buf[++top] = x % 10 + '0';} while (x /= 10);
	while (top) putchar(OPT::buf[top--]);
	if (pt) putchar(aft);
}

const int maxn = 100010;
const int maxt = 1000010;
const int MOD = 1000000007;

int n;
int MU[maxn];
ll frog[maxt];
std::vector<int>p;

int main() {
	freopen("1.in", "r", stdin);
	qr(n);
	for (rg int i = 1; i <= n; ++i) qr(MU[i]);
	frog[0] = 1;
	for (rg int i = 1; i <= n; ++i) {
		p.clear();
		for (rg int j = 1, sn = sqrt(MU[i]); j <= sn; ++j) if (!(MU[i] % j)) {
			int k = MU[i] / j;
			p.push_back(k); 
			if (k != j) p.push_back(j);
		}
		std::sort(p.begin(), p.end());
		for (rg int j = p.size() - 1; ~j; --j) {
			frog[p[j]] = (frog[p[j]] + frog[p[j] - 1]) % MOD;
		}
	}
	ll ans = 0;
	for (rg int i = 1; i <= n; ++i) ans = (ans + frog[i]) % MOD;
	qw(ans, '\n', true);
	return 0;
}

Summary

一个数的因数个数是 O(n) 是一个非常松的上界,事实上,在100万范围内因数个数最多的数的因数不过240个。遇到更大的范围可以 O(nlnn) 筛出所有数的因数来取得实际个数。

posted @   一扶苏一  阅读(562)  评论(0编辑  收藏  举报
编辑推荐:
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
· 基于Microsoft.Extensions.AI核心库实现RAG应用
· Linux系列:如何用heaptrack跟踪.NET程序的非托管内存泄露
· 开发者必知的日志记录最佳实践
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
点击右上角即可分享
微信分享提示