1. 程式人生 > >Longest Increasing Subsequence HDU - 6284

Longest Increasing Subsequence HDU - 6284

/*
首先預處理好f g陣列
 fi :以a[i]為結尾的 最長上升子序列的長度
 gi :以a[i]為開始的 最長上升子序列的長度
 mxx : 最長上升子序列的長度 
線段樹優化 nlogn
(不包含a[i]==0)

顯然把所有0換成x  只可能是mxx變成mxx+1 

 然後我們考慮一對 i j (下標)
 若 f[i]+g[j]==mxx 則 所有a[i]+1~~~a[j]-1之間的x 
 他們對用的lis長度為mxx+1 
 然後列舉i j涼了
 對於一個i 我們只需要找到 他後面的 一個j 滿足  f[i]+g[j]==mxx 並且a[j]最大
 然後維護bg[i] 表示長度為g[j]==i的所有的 a[j]中最大的 
 從後往前列舉i 然後維護 bg O(1)轉移 
 上述過程可能 i和bg維護的j之間 他沒有0 那就不能轉移
 所以 按0分段  遇到0 就把之前的資訊更新bg 
 
 然後沒了 
  
*/ #include<cstdio> #include<iostream> #include<cstdlib> #define lc k*2 #define rc k*2+1 #define mid (l+r)/2 #define maxn 400010 #define ll long long using namespace std; ll n,a[maxn],f[maxn],s[maxn],g[maxn],as[maxn],bg[maxn],c[maxn][2]; void Insert(ll k,ll l,ll r,ll x,ll y){
if(x==l&&r==x){ s[k]=max(s[k],y);return; } if(x<=mid)Insert(lc,l,mid,x,y); else Insert(rc,mid+1,r,x,y); s[k]=max(s[lc],s[rc]); } ll Query(ll k,ll l,ll r,ll x,ll y){ if(x>y)return 0; if(x<=l&&y>=r)return s[k]; ll res=0; if(x<=mid)res=max(res,Query(lc,l,mid,x,y));
if(y>mid)res=max(res,Query(rc,mid+1,r,x,y)); return res; } int main(){ while(~scanf("%lld",&n)){ for(ll i=0;i<=n*4;i++) s[i]=f[i]=g[i]=as[i]=0; for(ll i=1;i<=n;i++){ scanf("%lld",&a[i]); //a[i]=rand(); f[i]=1;g[i]=1; } ll mxx=1; for(ll i=1;i<=n;i++){ if(a[i]==0)continue; ll mx=Query(1,1,n,1,a[i]-1); f[i]=mx+1;mxx=max(mxx,f[i]); Insert(1,1,n,a[i],f[i]); } for(ll i=0;i<=n*4;i++)s[i]=0; for(ll i=n;i>=1;i--){ if(a[i]==0)continue; ll mx=Query(1,1,n,a[i]+1,n); g[i]=mx+1;Insert(1,1,n,a[i],g[i]); } for(ll i=0;i<=n*4;i++)bg[i]=0; ll cnt=0;a[0]=-1; for(ll i=n;i>=0;i--){ if(a[i]==0){ for(ll j=1;j<=cnt;j++) bg[c[j][0]]=max(bg[c[j][0]],c[j][1]); cnt=0;bg[0]=n+1; } else{ ll mx=bg[mxx-f[i]]; c[++cnt][0]=g[i];c[cnt][1]=a[i]; if(mx-1<a[i]+1)continue; as[a[i]+1]++;as[mx]--; } } ll ans=0; for(ll i=1;i<=n;i++)as[i]+=as[i-1]; for(ll i=1;i<=n;i++){ if(as[i]>0)ans+=i*(mxx+1); else ans+=i*mxx; //("%lld\n",ans); } printf("%lld\n",ans); } return 0; }