题目传送门
分析
1e5的数据,要nlogn才能过
第一问求的是 最长不上升序列, 第二问求的是 最少的不上升子列个数
第一问:
传统的dp求LIS是 \(n^2\) 的复杂度,事实上第二层循环可以优化到logn
这里针对本题阐述一下口胡一下
对于第i个数x,如果x小于等于序列当前元素时,直接加入到序列中
如果x大于序列当前元素时,在序列中二分查找到小于x的最大值的位置,用x替换掉原来的值
ps:可以注意到此时保存的序列很大可能就不是LIS序列,但是它的最终的长度一定是LIS长度
这样子更新可以理解为既不破坏原来的最长序列的结构,同时也能使该序列具有更大的延续性(因为用了一个更大的x接替了原来小一点的数)
但是这样子最终只能得到LIS的长度,没法得到序列,目前还没有想出求序列的办法
第二问:
luogu题解中很多可以证明实际上就是求最长公共上升序列的,但是我不会证明
和第一问的logn处理有类似的联想,我想到的是对于每一个新加入的数x,应该找到各系统中当前高度大于等于x的最小值,把x接在后面
最优性解释是如果把x放在了另外一个系统上,那么下一个比x大一点的数就不一定能放到当前系统中
以上用数学表示就是,存在两个系统当前高度为a, a + 1;
此时新加入的一个数x = a,那么把x放在高度为a的系统肯定比放在高度为a + 1的系统更优,
因为如果下一个数x = a + 0.5,此时就没办法放到当前的系统中了。
过程可以用平衡树维护,STL中set还可以做到直接查询大于等于x的最小值的位置,见代码
代码
#include <iostream>
#include <cstdio>
#include <set>
using namespace std;
#define rg register
inline int read(){
rg int x = 0, f = 1;
rg char c = getchar();
while (c < '0' || c > '9') {if (c == '-') f = -1; c = getchar();}
while (c >= '0' && c <= '9') x = (x << 1) + (x << 3) + (c ^ 48), c = getchar();
return x * f;
}
const int N = 1e5 + 1, max_h = 5e4 + 1;
int n, prein;
int a[N];
set<int> q;
int missile[N];
inline void init(){
while (scanf("%d", &prein) != EOF)
a[++n] = prein;
}
inline void doit(){
int sum = 0;
missile[0] = max_h;
for (int i(1); i <= n; ++i){
if (a[i] <= missile[sum]) missile[++sum] = a[i];
else {
int l = 1, r = sum;
while (l < r){
int mid = l + r >> 1;
if (missile[mid] < a[i]) r = mid;
else l = mid + 1;
}
missile[l] = a[i];
}
set<int>::iterator it = q.lower_bound(a[i]);
if (it != q.end())
q.erase(it);
q.insert(a[i]);
}
printf("%d\n%d", sum, q.size());
}
int main(){
ios::sync_with_stdio(false);
cout.tie(NULL);
init();
doit();
return 0;
}
标签:普及,int,missile,rg,NOIP1999,LIS,序列,拦截,include
From: https://www.cnblogs.com/cancers/p/17035327.html