UVA 1471 Defense Lines (STL + 二分)

    xiaoxiao2021-03-26  4

    大体题意:

    给你一个长度为n(n < 2e5)的序列,你的任务是删除一个连续的子序列,使得剩下的序列中有一个长度最大的连续递增子序列。求最大序列长度?

    思路:

    因为要删除一个连续的子序列,所以会分成左右两部分之和的形式,我们枚举右边一部分的起点位置i,快速的找到左边一个合适的位置j,使得a[j] < a[i] 并且 g[j]尽量大。

    (注: g[i]表示以i 位置结束的最长上升连续子序列的长度,f[i]表示以i位置开始的最长上升连续子序列的长度)

    这样我们可以利用set 来维护当前合法的左边位置的值。

    什么是合法的?  必须是a[i] > a[j] 并且 g[i] > g[j], 这样我们可以按照a[i]排序,那么g[i]也一定是升序的, 

    这样我们加入一个  就删除不合法的。

    利用set里的二分查找lower_bound  便很快的查到合法的位置了。

    详细见代码:

    #include <cstdio> #include <cstring> #include <algorithm> #include <set> #define Max(a,b)((a)<(b)?(b):(a)) using namespace std; const int maxn = 2e5 + 10; int T, n; int g[maxn], f[maxn], a[maxn]; struct Node{ int id; int len; Node(int id = 0,int len = 0):id(id),len(len){} bool operator < (const Node& rhs) const { return a[id] < a[rhs.id] || (a[id] == a[rhs.id ] && len < rhs.len); } bool operator == (const Node & rhs) const { return id == rhs.id && len == rhs.len; } }; set<Node>s; set<Node>::iterator it,it2,it3; int main(){ scanf("%d",&T); while(T--){ scanf("%d",&n); s.clear(); for (int i = 1; i <= n; ++i)scanf("%d",a+i); g[1] = 1; for (int i = 2; i <= n; ++i){ if (a[i] > a[i-1]){ g[i] = g[i-1] + 1; } else g[i] = 1; } f[n] = 1; for (int i = n-1; i >= 1; --i){ if (a[i] < a[i+1] ) f[i] = f[i+1] + 1; else f[i] = 1; } int ans = 1; for (int i = 1; i <= n; ++i)ans = Max(ans,f[i]); s.insert(Node(1,1)); for (int i = 2; i <= n; ++i){ Node v = Node(i,g[i]); it = s.lower_bound(v); if (it != s.begin()){ --it; Node u = *it; ans = Max(ans,u.len + f[i]); ++it; } --it; s.insert(v); it = s.find(v); it2 = it; if (it2 != s.begin()){ --it2; Node u = *it2; if (a[u.id] <= a[v.id]){ if (u.len>= v.len) { s.erase(it); continue; } } } it2 = it; ++it2; for ( ;it2 != s.end(); ){ Node u = *it2; if (a[u.id] >= a[v.id]){ if (u.len <= v.len) it2 = s.erase(it2); else break;; } } } printf("%d\n",ans); } return 0; } /** 2 9 5 3 4 9 2 8 6 7 1 7 1 2 3 10 4 5 6 **/

    转载请注明原文地址: https://ju.6miu.com/read-500257.html

    最新回复(0)