Hướng dẫn giải của Cáp treo 3
Nộp một lời giải chính thức trước khi tự giải là một hành động có thể bị ban.
Xét thuật toán tính LIS, ~d_x~ là giá trị cuối cùng nhỏ nhất của dãy con có độ dài ~x~. Khi thêm vào một số ~a_i~ ta cần tìm giá trị ~d_j < a_i~ sao cho ~j~ là lớn nhất rồi gán ~d_{j}=a_i~. Ở bài toán này cần xem xét đến toàn bộ các dãy con tăng nên thay vì lưu ~d_x~ là một giá trị thì lưu là một mảng, thay vì gán ~a_i~ thì cho vào cuối của mảng. Khi tìm ~j~ thì xét phần tử cuối của mảng.
Nhận xét rằng trong mảng ~d_x~ bất kì thì giá trị không bao giờ tăng vì nếu tăng giá trị đó sẽ ở ~d_y~ với ~y>x~. Tạo một những mảng ~f_x~ tương ứng với từng phần tử của các ~d_x~ là số lượng các dãy con tăng kết thúc ở giá trị đó, ~p_x~ là mảng tổng tiền tố của ~f_x~ (dùng tổng tiền tố để tính nhanh hơn). Kết quả là phần tử cuối cùng của ~p_k~ với ~k~ là độ dài LIS.
Code:
#include <bits/stdc++.h>
using namespace std;
#define pi pair<int, int>
#define inf 0x3f3f3f3f
#define all(x) x.begin(), x.end()
#define int long long
#define setinf(d) memset(d, inf, sizeof d)
#define setneg(d) memset(d, -1, sizeof d)
#define set0(d) memset(d, 0, sizeof d)
#define Log2(x) 63 - __builtin_clzll(x)
#define oo 2e18
#define mod 1000000007
const int maxn = 1e5 + 5;
int n, ans = 0;
int a[maxn];
vector<int> d[maxn];
vector<int> pre[maxn];
vector<int> v;
signed main(){
ios_base::sync_with_stdio(false); cin.tie(0); cout.tie(0);
cin >> n;
for(int i = 1; i <= n; i++) cin >> a[i], v.push_back(a[i]);
sort(all(v));
v.erase(unique(all(v)), v.end());
for(int i = 1; i < maxn; i++){
d[i].push_back(oo);
pre[i].push_back(0);
}
d[0].push_back(oo);
d[0].push_back(-oo);
pre[0].push_back(0);
pre[0].push_back(1);
for(int i = 1; i <= n; i++){
int l = 1, r = n, lis = 1;
while(l <= r){
int m = (l + r) >> 1;
if(d[m - 1].back() < a[i]) lis = m, l = m + 1;
else r = m - 1;
}
ans = max(ans, lis);
l = 0, r = d[lis - 1].size(); int p = 0;
while(l <= r){
int m = (l + r) >> 1;
if(d[lis - 1][m] < a[i]) p = m, r = m - 1;
else l = m + 1;
}
d[lis].push_back(a[i]);
pre[lis].push_back((pre[lis].back() + pre[lis - 1].back() - pre[lis - 1][p - 1] + mod) % mod);
}
cout << pre[ans].back();
}