问题描述
X 校最近打算美化一下校园环境。前段时间因为修地铁,X 校大门外种的行道树全部都被移走了。现在 X 校打算重新再种一些树,为校园增添一抹绿意。
X 校大门外的道路是东西走向的,我们可以将其看成一条数轴。在这条数轴上有 n 个障碍物,例如电线杆之类的。虽然障碍物会影响树的生长,但是障碍物不一定能被随便移走,所以 X 校规定在障碍物的位置上不能种树。n 个障碍物的坐标都是整数;如果规定向东为正方向,则 n 个障碍物的坐标按照从西到东的顺序分别为 a1,a2,⋯,an。X 校打算在 [a1,an] 之间种一些树,使得这些树看起来比较美观。
X 校希望,在一定范围内,树应该是等间隔的。更具体地说,如果把 [a1,an) 划分成一些区间 [ap1,ap2),⋯,[apm−1,apm)(1=p1<p2<⋯<pm=n),那么每个区间 [api,api+1) 内需要至少种一棵树,且该区间内种的树的坐标连同区间端点 api,api+1 应该构成一个等差数列。不同区间的公差,也就是树的间隔可以不相同。
例如,如果障碍物位于 0,2,6 这三处,那么我们可以选择在 [0,2) 和 [2,6) 分别种树,也可以选择在 [0,6) 等间隔种树。如果是分别在 [0,2) 和 [2,6) 种树,由于每个区间内至少要种一棵树,坐标 1 上必须种树;而 [2,6) 上的树可以按照 1 的间隔种下,也可以按照 2 的间隔种下。下图表示了这两种美观的种树方案,其中橙色的圆表示障碍物,绿色的圆表示需要在这个位置种树,箭头上的数字表示种下这棵树时对应的间隔为多少。
对区间 [0,2) 和 [2,6) 分别以 1 和 2 的间隔种树是美观的
对区间 [0,2) 和 [2,6) 分别以 1 的间隔种树也是美观的
而如果选择在 [0,6) 区间等间隔种树,我们只能以 3 的间隔种树,因为无论是选择间隔 1 或者间隔 2,都需要在坐标 2 上种树,而这个位置已经有障碍物了。下图分别表示了间隔为 3,2,1 时的种树情况,红色箭头表示不能在这里种树。
对区间 [0,6) 以 3 的间隔种树是美观的
对区间 [0,6) 以 2 的间隔种树是不美观的
对区间 [0,6) 以 1 的间隔种树也是不美观的
一般地,给定一个区间 [al,ar),对于树的坐标的集合 T⊂(al,ar)(T⊂Z),归纳定义 T 在 [al,ar) 上是美观的:
- 如果 T≠∅,T∩{al,al+1,⋯,ar}=∅,并且存在一个公差 d≥1,使得 T∪{al,ar} 中的元素按照从小到大的顺序排序后,可以构成一个公差为 d 的等差数列(显然,这个等差数列的首项为 al,末项为 ar),则 T 在 [al,ar) 上是美观的;
- 如果 T∩{al,al+1,⋯,ar}=∅,并且存在一个下标 m(l<m<r),使得 T∩(al,am) 在 [al,am) 上是美观的,且 T∩(am,ar) 在 [am,ar) 上是美观的,则 T 在 [al,ar) 上是美观的。
根据这一定义,空集在任意区间上都不是美观的;另外,如果存在下标 i 使得 ai∈T,那么 T 一定不是美观的。
我们称两种种树的方案是本质不同的,当且仅当两种方案中,种树的坐标集合不同。请帮助 X 校对 [a1,an) 求出所有本质不同的美观的种树方案。当然,由于方案可能很多,你只需要输出总方案数对 109+7 取模的结果。
输入格式
输入的第一行包含一个正整数 n,表示障碍物的数量。
输入的第二行包括 n 个非负整数 a1,⋯,an,表示每个障碍物的坐标。
保证对 i=1,2,⋯,n−1,ai<ai+1。
输出格式
输出一个非负整数,表示本质不同的美观的种树方案的数量对 109+7 取模的结果。
评测用例规模与约定
对于 10% 的数据,保证 n=2;
对于 30% 的数据,保证 n≤10;
对于 60% 的数据,保证 n≤100,ai≤1000;
对于 100% 的数据,保证 2≤n≤1000,0≤ai≤100,000,且至少存在一种美观的种树方案。
思路
这题看到题目其实可以感觉到应该用动态规划解决。我们定义f[i]为前i个障碍物的美观的种树方案总数量。则可以列出式子,其中cnt为
的美观的种树方案总数量。其实在找的时候可以发现这就是在找两个障碍物之间距离的因子,同样的因子可能会被多次用到,为了提高效率,我们可以将因子预处理一下,根据题目可以知道ai范围,通过遍历将其范围内因子都先处理下,同时,我们也可以发现在找因子时j可以从i-1开始慢慢递减,因为这样可以有效处理,之前找过的因子不可以再使用,不然会导致在障碍物上种树,之前找到的障碍物到右端点障碍物(也就是ai)的距离也不可以作为因子。
代码
#include <bits/stdc++.h>
using namespace std;
const int N = 1005,mod = 1e9 + 7;
vector<int> pre[100005];
long long int f[N];
int a[N];
bool vis[100005];
int main()
{
for(int i = 1; i <= 1e5; i++) {
for(int j = 2*i; j <= 1e5; j += i) {
pre[j].push_back(i);
}
}
int n;
cin >> n;
for(int i = 1; i <= n; i++) {
cin >> a[i];
}
f[1] = 1;
for(int i = 2; i <= n; i++) {
for(int j = 1; j <= a[i]; j++) {
vis[j] = false;
}
for(int j = i - 1; j >= 1; j--) {
int dis = a[i] - a[j];
long long int cnt = 0;
for(int k = 0; k < pre[dis].size(); k++) {
if(!vis[pre[dis][k]])
{
vis[pre[dis][k]] = true;
cnt++;
}
}
vis[dis] = true;
f[i] = (f[i] + f[j] * cnt) % mod;
}
}
cout << f[n] <<endl;
return 0;
}