我同意 Ante 的重复,虽然我使用了一个稍微简化的版本。请注意,我使用字母 P 来命名最大产品,因为它在 the original problem statement 中使用:
f(1,x) = 1
f(i,x) = sum(f(i-1, y) for y in {1, ..., floor(P/x)})
f(i,x) 是以 x 结尾的长度为 i 的序列的数量。那么问题的答案就是f(n+1, 1)。
当然,因为在这个任务中 P 可以达到 10^9,所以直接使用 DP 表实现是不可能的。但是,floor(P/i) 的不同值最多只有 m aj ... bj,其中 floor(P/aj sub>) = floor(P/bj).我们可以使用二进制搜索在O(段数* log P)中找到这些段。
想象一下 f 的完整 DP 表。由于floor(P/x)只有m个不同的值,所以f的每一行都由m组成具有相同值的连续范围。
所以让我们计算 压缩 DP 表,其中我们将行表示为(长度,值)对的列表。我们从 f(1) = [(P, 1)] 开始,我们可以通过按递增顺序处理段并计算存储在 f(i) 中的长度的前缀和来从 f(i) 计算 f(i+1) .
我实现这种方法的总运行时间是O(m (log P + n))。这是我使用的代码:
using ll=long long;
const int mod = 1000000007;
void add(int& x, ll y) { x = (x+y)%mod; }
int main() {
int n, P;
cin >> n >> P;
int x = 1;
vector<pair<int,int>> segments;
while(x <= P) {
int y = x+1, hi = P+1;
while(y<hi) {
int mid = (y+hi)/2;
if (P/mid < P/x) hi=mid;
else y=mid+1;
}
segments.push_back(make_pair(P/x, y-x));
x = y;
}
reverse(begin(segments), end(segments));
vector<pair<int,int>> dp;
dp.push_back(make_pair(P,1));
for (int i = 1; i <= n; ++i) {
int j = 0;
int sum_smaller = 0, cnt_smaller = 0;
vector<pair<int,int>> dp2;
for (auto it : segments) {
int value = it.first, cnt = it.second;
while (cnt_smaller + dp[j].first <= value) {
cnt_smaller += dp[j].first;
add(sum_smaller,(ll)dp[j].first*dp[j].second);
j++;
}
int pref_sum = sum_smaller;
if (value > cnt_smaller)
add(pref_sum, (ll)(value - cnt_smaller)*dp[j].second);
dp2.push_back(make_pair(cnt, pref_sum));
}
dp = dp2;
reverse(begin(dp),end(dp));
}
cout << dp[0].second << endl;
}
我需要对数组的处理进行一些微优化以获得 AC,但这些都不是真正相关的,所以我放弃了它们。