Longest Increasing Subsequence HDU
阿新 • • 發佈:2018-12-12
思路:因為改變的數是同一個,所以最後對LIS的貢獻最多隻能是1,所以可以先求出最長上升子序列長度,然後每個改變的數,考慮它對LIS的加成是1還是0.
設a[i]表示已第i項結束的最長上升子序列長度,b[i]表示以第i項為起點的最長上升子序列長度。
考慮在0的左邊找一個a[i],在0的右邊找一個b[j](並且c[i]<c[j]),如果a[i]+b[j]==L,那麼將這個0改成其他數字,就有可能使得LIS增加。
下面考慮如何找這樣的對數。
首先找這樣的對數一定是在0的兩邊開始找的,如果在同一邊,這時沒有意義的。
設f[i]表示長度為i的,起點最大的c[i]。找最大的開始點是因為,假設在0的左邊有a[i],在0的右邊有多個b[j]滿足a[i]+b[j]==L,這時我們只有找一個最大的c[j]就行,因為它可以包含其他的。
下面介紹O(n)的方法求出滿足的。
設一個區間d[] 如果d[i]>0表示i這個值有效,假設我們找到一個區間[a,b]表示a到b之間的數都可以滿足+1,那麼我們可以這樣考慮,讓d[a+1]++,d[b]--,表示在a+1以前都可以滿足,而超出b以後,就不行,所以立刻-1.這樣我們求一個求字首和就清楚了,如果大於0,表示滿足,==0,表示不滿足。
這個處理技巧非常巧妙。具體實現看程式碼和註釋吧。
程式碼:
#include<bits/stdc++.h> #define N 100010 #define LL long long using namespace std; const LL mod=1e9+7; int a[N],b[N],c[N],f[N],d[N]; int main() { int n; while (~scanf("%d",&n)) { for (int i=0;i<=n;i++)a[i]=b[i]=d[i]=f[i]=0; for (int i=1;i<=n;i++) scanf("%d",&c[i]); int k=1; while (c[k]==0 && k<=n) k++; f[1]=c[k];int t=0;a[k]=1; if (k<=n)t=1; for (int i=k+1;i<=n;i++) if (c[i]) { int j=lower_bound(f+1,f+t+1,c[i])-f; f[j]=c[i]; a[i]=j; //end if (j==t+1) t++; } k=n; while (c[k]==0 && k) k--; f[1]=-c[k];t=0; b[k]=1; if (k>0)t=1; for (int i=k-1;i>0;i--) if (c[i]) { int j=lower_bound(f+1,f+t+1,-c[i])-f; f[j]=-c[i]; b[i]=j; //begin if (j==t+1) t++; } for (int i=0;i<=n;i++)f[i]=0; k=n; //f[0]=n+1; while(k>0) { int y=k; for (;k>0;k--) { if (!c[k]){f[0]=n+1;break;} int u=t-a[k]; if (f[u]-1>c[k]) // 找到符合要求的,將其進行標記 { d[c[k]+1]++; d[f[u]]--; } } for (int i=y;i>k;i--) if (f[b[i]]<c[i]) f[b[i]]=c[i];//更新f的最大值 if (f[t]-1>c[k] && k) //如果找到一個,以它為起點的LIS長度==L,那麼在這個0的位置放上1到f[t],必然可以增加LIS. { d[c[k]+1]++; d[f[t]]--; } k--; } for (int i=1;i<=n;i++) d[i]+=d[i-1]; LL ans=0; for (int i=1;i<=n;i++)if (!d[i])ans+=(LL) i*(t);else ans+=(LL) i*(t+1); printf("%I64d\n",ans); } }