【HDU4521】 dp思想+线段树操作
题目链接:http://acm.hdu.edu.cn/showproblem.php?pid=4521
题目大意:有n个数,求间距大于d的最长递增子序列。
解题思路:扎眼一看,不是简单的dp嘛,再眨眼一看,n最大为10^5,蛋碎一地呀。
这题不仅要线段树功底好,还要有比较强的dp思想,从第d+1个位置开始更新,每个节点的sum值保存的是从第1个数到当前数的最长符合要求的子序列,查询的时候只需查询线段树中值在其左边的最大的sum[u](最长符合要求序列)。
1 #include <iostream> 2 #include <cstdio> 3 #include <cstring> 4 #include <queue> 5 #include <algorithm> 6 using namespace std; 7 8 #define lz 2*u,l,mid 9 #define rz 2*u+1,mid+1,r 10 const int maxn=110000; 11 int sum[2*maxn], dp[maxn+5], a[maxn+5]; 12 13 void Update(int u, int l, int r, int p, int c) 14 { 15 sum[u]=max(sum[u],c); 16 if(l==r)return ; 17 int mid=(l+r)>>1; 18 if(p<=mid) Update(lz,p,c); 19 else Update(rz,p,c); 20 } 21 22 int Query(int u, int l, int r, int tl, int tr) 23 { 24 if(tl>tr) return 0; 25 if(tl<=l&&r<=tr) return sum[u]; 26 int mid=(l+r)>>1; 27 if(tr<=mid) return Query(lz,tl,tr); 28 else if(tl>mid) return Query(rz,tl,tr); 29 else 30 { 31 int t1=Query(lz,tl,mid); 32 int t2=Query(rz,mid+1,tr); 33 return max(t1,t2); 34 } 35 } 36 37 int main() 38 { 39 int n, d; 40 while(scanf("%d%d",&n,&d)!=EOF) 41 { 42 memset(sum,0,sizeof(sum)); 43 for(int i=1; i<=n; i++) 44 scanf("%d",a+i); 45 int maxx=0; 46 for(int i=1; i<=n; i++) 47 { 48 if(i>d+1) Update(1,1,maxn,a[i-d-1]+1,dp[i-d-1]); ///注意a[i-d-1]要加1 49 dp[i]=Query(1,1,maxn,1,a[i])+1; 50 maxx=max(dp[i],maxx); 51 } 52 printf("%d\n",maxx); 53 } 54 return 0; 55 }