本题的题意就是从长为n的串中截取一段,把剩下的两端拼接起来构成一个可以够成的最长连续上升子序列。
首先本题若直接按照题意来先枚举截取的起点和终点在数一数就需要O(n^3)时间复杂度。n为2*10^5太大。
价格预处理就可以避免数一数、
以f(i) 表示以i位置为开头的连续上升序列的长度。g(i)表示以i位置为结尾的连续上升序列的长度。
然后只需优化枚举环节。 可以只枚举右端点,快速找到一个比之值小而且g(i)尽可能大的点j。
如何做到这一点,观察i点之前的(a[ j ] , g[ j ]) 二元组,如果 a[ k ] > a[ t ] && g[ i ] <g[ t ] (本题t,k的大小关系就无关紧要了)那么k位置一定不是最优的候选点。那么剩下的有保留价值的二元组的
从小到大排序后,特点为以a为长,g值为宽,如同一个大矩形套着一个小矩形如此一层层套着。
两个值都持增势。所以可以用经典lis 的nlogn方法解题。
#include <cstdio> #include <cstring> #include <cstdlib> #include <iostream> #include <vector> #include <deque> #include <set> using namespace std; const int maxn = 200110; int n,a[maxn],f[maxn],g[maxn],s[maxn]; int main() { int T; scanf("%d",&T); while(T--){ scanf("%d",&n); for(int i=1;i<=n;i++){ scanf("%d",&a[i]); if(i==1) f[1] = 1; else if(a[i] > a[i-1]) f[i]=f[i-1]+1; else f[i] = 1; } for(int i=n;i>=1;i--){ if(i==n) g[i] = 1; else if(a[i]<a[i+1]) g[i] = g[i+1]+1; else g[i] = 1; } for(int i=1;i<=n;i++) s[i] = (1e9) + 10; s[1] = a[1]; int res = 1; for(int i=2;i<=n;i++){ int p = lower_bound(s+1,s+n+1,a[i])-s; res = max(res,p-1+g[i]); if(a[i]<s[f[i]]) s[f[i]]=a[i]; } printf("%d\n",res); } return 0; } /* 2 9 5 3 4 9 2 8 6 7 1 7 1 2 3 10 4 5 6 */ /* 2 9 5 3 4 9 2 8 6 7 1 7 1 2 3 10 4 5 6 */