問題描述
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 取模的結果。
樣例輸入
3
0 2 6
Data
樣例輸出
3
Data
樣例說明
這組樣例即為題面描述中提到的那組。
樣例輸入
11
0 10 20 30 40 50 60 70 80 90 100
Data
樣例輸出
256507
Data
樣例輸入
333
33 44 67 210 528 762 873 984 1234 1466 1739 2859 3421 4061 4598 5172 5201 5220 5261 5322 5389 5559 6670 7070 7898 8079 8129 8192 8616 8641 8806 9559 9585 9750 10263 10627 10674 10692 10903 11649 11885 12179 12307 12743 13173 13352 13389 13496 13611 15292 15321 16018 16327 16415 16959 16972 17499 17617 17786 18476 18966 19239 19498 19875 20312 20392 21603 21620 21730 21967 21972 21999 22015 22590 22775 23709 23839 24165 24408 24595 25160 25479 25812 26482 27328 28101 28297 28305 28342 28557 28986 29110 29401 29765 30292 30493 30739 31027 31201 31218 31414 32089 32759 32770 32777 32815 32877 32890 33297 33457 33603 33757 33866 34498 34525 34659 34679 34861 34870 34997 35311 35846 36411 36457 36738 36902 37940 38228 40156 40320 40705 40737 40803 41066 41443 41460 41954 41968 42040 42062 42099 43281 43320 43527 43537 43587 43729 44750 44822 45655 45769 46109 46525 47060 47128 47999 48635 48887 48981 49366 49424 49524 50546 50580 50689 51332 51861 51943 52097 52702 53009 53067 53397 53526 53901 54280 54399 54801 55535 55592 55740 55843 56110 56428 56552 56682 56848 57179 57688 57797 57847 57959 58330 58831 59553 59699 59884 59939 61233 61636 61732 61908 62145 62549 62649 62740 62912 62971 63053 64312 64322 64412 64816 64845 64873 64923 64976 65023 65166 65496 66065 66491 66803 66941 67081 68331 68336 68360 68476 69179 69719 69758 69948 70072 70544 70598 70990 71014 71454 71687 71743 71958 72282 72384 72456 72985 73327 74325 75046 75097 76647 77062 77088 77431 77553 77673 77753 78217 78518 78564 79565 79588 79686 80275 80939 81052 81348 81386 81440 81589 81610 81793 82408 82801 82836 83239 83466 83610 83867 83943 84441 84467 85248 85305 85554 85565 85758 86251 86603 86743 87323 87565 87824 87833 88265 88309 89178 89509 89618 89699 89708 90331 90359 90878 90902 91449 92284 92374 92549 92609 93609 94345 94934 95140 95475 95733 95985 95995 96270 96641 96807 97003 97632 98160 98677 98853 98943 99037 99055 99075 99185 99395 99592
Data
樣例輸出
7094396
Data
評測用例規模與約定
對於 10% 的數據,保證 n=2;
對於 30% 的數據,保證 n≤10;
對於 60% 的數據,保證 n≤100,ai≤1000;
對於 100% 的數據,保證 2≤n≤1000,0≤ai≤100,000,且至少存在一種美觀的種樹方案。
不難打出 \(n^2mlogm\)的暴力dp然后喜提60分。設dp[i]為到第i個障礙物的方案總數,有轉移方程\(dp[i] = \Sigma_{j = 1}^{i - 1}dp[j]\times calc(j, i)\),其中calc(j, i)為第j個障礙物到第i個障礙物之間的方案數。關鍵在於怎么優化calc函數。 首先第j個障礙物到第i個障礙物之間的間隔必須為a[i] - a[j]的因子(顯然),方案數一定小於等於因子個數,其次這個間隔還不能撞上障礙物。不妨換個角度,倒着從i - 1開始枚舉j,這樣一開始i - 1和i之間是沒有障礙物的,則a[i] - a[i - 1]的所有因子都滿足條件,更新答案並把它們添加到一個集合中;然后到了i - 2,同樣先枚舉a[i] - a[i - 2]的所有因子,這些因子中已經處於集合中的一定不可,因為按這樣的間隔排列一定會有樹遇上a[i - 1]這個障礙物。因此倒着枚舉到a[i] - a[k]因子的時候如果已經在集合則跳過,如果不在集合則更新數量同時把這個因子插入集合。不要忘記外循環每一次都要先把集合清空,以及要篩法預處理出1e5范圍內每個數的因子。
#include <bits/stdc++.h>
#define int long long
#define mod 1000000007
using namespace std;
int n, a[1005], dp[1005];//a數組是單調遞增的
set<int> st;
vector<int> f[100005];
int calc(int p, int q) {//從a[x]到a[y]的可能的方案數(中間可能有障礙
int x = a[q] - a[p];
int ans = 0;
//!
st.insert(x);
for(int i = 0; i < f[x].size(); i++) {
if(st.find(f[x][i]) != st.end()) {
continue;
} else {
ans++;
st.insert(f[x][i]);
}
}
return ans;
}
signed main() {
ios::sync_with_stdio(false);
cin >> n;
for(int i = 1; i <= n; i++) {
cin >> a[i];
}
for(int i = 1; i <= a[n] / 2; i++) {
for(int j = 2 * i; j <= a[n]; j += i) {
f[j].push_back(i);
}
}
dp[1] = 1;
for(int i = 1; i <= n; i++) {
st.clear();
for(int j = i - 1; j >= 1; j--) {
dp[i] = (dp[i] + dp[j] * calc(j, i) % mod) % mod;
}
}
cout << dp[n] % mod;
return 0;
}