uva1471 Defense Lines
#include<bits/stdc++.h> #define REP(i,a,b) for(int i=a;i<=b;i++) #define MS0(a) memset(a,0,sizeof(a)) using namespace std; typedef long long ll; const int maxn=1000100; const int INF=1<<29; int n,a[maxn]; int f[maxn],g[maxn]; struct Node { int x,y; friend bool operator<(Node A,Node B) { return A.x<B.x; } };Node q[maxn];int len; int main() { freopen("in.txt","r",stdin); int T;cin>>T; while(T--){ scanf("%d",&n); REP(i,1,n) scanf("%d",&a[i]); if(n==0){ cout<<0<<endl; continue; } g[1]=1;REP(i,2,n) g[i]=a[i]>a[i-1]?g[i-1]+1:1; f[n]=1;for(int i=n-1;i>=1;i--) f[i]=a[i]<a[i+1]?f[i+1]+1:1; //cout<<n<<endl;REP(i,1,n) cout<<a[i]<<" ";cout<<endl; //REP(i,1,n) cout<<f[i]<<" ";cout<<endl; //REP(i,1,n) cout<<g[i]<<" ";cout<<endl; int ans=1; q[len=1]={a[1],g[1]}; REP(i,2,n){ Node cur={a[i],g[i]}; if(cur.x>q[len].x){ ans=max(ans,f[i]+q[len].y); if(cur.y>q[len].y) q[++len]=cur; } else{ int k=lower_bound(q+1,q+len+1,cur)-q; if(k>1) ans=max(q[k-1].y+f[i],ans); else ans=max(ans,f[i]); if(cur.x==q[k].x&&cur.y<q[k].y) continue; if(cur.x<q[k].x&&cur.y<q[k].y) continue; q[k]=cur; } //cout<<i<<" "<<ans<<endl; } cout<<ans<<endl; } return 0; } /** 题意: 给定一个长度为n的序列,求随便去掉一段后(可以不去),序列的最长连续递增子序列长度。 这题我先按紫书上的思路写的,那个思路的正确性是显然的,但是需要用set,在这种二元组中 用set是非常恶心的,很容易出错,因此我写挂了。后来换了另一种思路,直接按照lis的 写法,调了一下细节就过了。 思路: 先分别预处理出以a[i]为开头和结尾的连续子序列长度f[i]和g[i], 接着按照基本的思路,枚举f[i],快速找到符合条件且最优的g[j],然后更新答案,将a[i],g[i]添加进去。 由于无法划窗,快速找到g[i]就二分了,那么这个时候自然就会想到lis的nlogn算法,更新状态直接替代就行了。 如何二分呢?当然是要看符合的条件和最优的单调性了,符合条件是指a[j]<a[i],最优就是 在符合条件的前提下使得g[i]尽可能大。 这里需要保持lis的单调性,如何保持单调性呢?这个思考过程我使用分类讨论的(智商太低无法一眼看出), 二分找到第一个>=cur的q[k],根据a和g的大小关系总共6种情况(等于也算),以此来确定是否应该用cur将k替换。 */
没有AC不了的题,只有不努力的ACMER!