http://acm.hdu.edu.cn/showproblem.php?pid=5489
2 5 2 1 2 3 4 5 5 3 5 4 3 2 1
Case #1: 3 Case #2: 1
/** hdu5489 ||2015合肥网络赛1006 dp+树状数组优化 题目大意:给定一个序列,求去掉一个连续长度为L的数后的最长上升子序列的长度 解题思路:用dp[i][0]表示前i个数不去L时的最长子序列,dp[i][1]表示前i个数去L时的最长子序列。 转移方程为:dp[i][0]:它可以由0到i-1中满足权值<=x_i的dp0_j转移到. dp[i][1]:它可以由0到i-1中满足权值<=x_i的dp1_j以及0到i-L-1中满足权值<=x_i的dp0_j转移到 由于数据范围是10^5,需要离散化一下数字,用树状数组维护。处理很巧妙,详见代码 */ #include <stdio.h> #include <string.h> #include <algorithm> #include <iostream> using namespace std; const int maxn=100100; int a[maxn],num[maxn],n,m,L; int dp[maxn][2],A[maxn],B[maxn]; bool cmp(int x,int y) { return a[x]<a[y]; } void add(int *A,int k1,int k2) { for(; k1<=m; k1+=(k1&(-k1)))A[k1]=max(A[k1],k2); } int find(int *A,int k1) { int ans=-1e9; for(; k1; k1-=(k1&(-k1)))ans=max(ans,A[k1]); return ans; } int main() { int T,tt=0; scanf("%d",&T); while(T--) { scanf("%d%d",&n,&L); for(int i=1; i<=n; i++)scanf("%d",&a[i]); a[0]=-2e9; for(int i=0; i<=n; i++)num[i]=i; sort(num,num+n+1,cmp); m=1; int pre=a[num[0]]; a[num[0]]=m; for(int i=1; i<=n; i++) { if(a[num[i]]!=pre)m++,pre=a[num[i]]; a[num[i]]=m; } // for(int i=0;i<=n;i++) printf("%d ",a[i]); // printf("\n"); for(int i=1; i<=m; i++)A[i]=0,B[i]=-1e9; if(L==0)for(int i=1; i<=m; i++)B[i]=0; for(int i=1; i<=n; i++) { dp[i][0]=find(A,a[i]-1)+1; dp[i][1]=find(B,a[i]-1)+1; add(A,a[i],dp[i][0]); add(B,a[i],dp[i][1]); if(i>=L) add(B,a[i-L],max(dp[i-L][0],dp[i-L][1])); } int ans=max(0,dp[n-L][0]); for(int i=1; i<=n; i++)ans=max(ans,dp[i][1]); printf("Case #%d: %d\n",++tt,ans); } return 0; }