3173: [Tjoi2013]最长上升子序列
题目:传送门
题解:
好题!
怎么说吧...是应该扇死自己...看错了两次题:
每次加一个数的时候,如果当前位置有数了,是要加到那个数的前面,而不是直接替代ORZ
那么我们可以用二分倒推出最终数列,顺便记录位置
那么最后问题就变成给你一列数,nlogn求最长上升子序列啦
代码:
1 #include<cstdio> 2 #include<cstring> 3 #include<cstdlib> 4 #include<cmath> 5 #include<algorithm> 6 using namespace std; 7 int n; 8 int a[110000],s[110000]; 9 int lowbit(int x){return x&-x;} 10 void change_max(int x,int p){while(x<=n)s[x]=max(s[x],p),x+=lowbit(x);} 11 void change_sum(int x){while(x<=n)s[x]++,x+=lowbit(x);} 12 int find_max(int x){int ans=0;while(x)ans=max(ans,s[x]),x-=lowbit(x);return ans;} 13 int find_sum(int x){int ans=0;while(x)ans+=s[x],x-=lowbit(x);return ans;} 14 int main() 15 { 16 scanf("%d",&n);for(int i=1;i<=n;i++)scanf("%d",&a[i]),a[i]++;//位置++ 17 for(int i=n;i>=1;i--)//倒推位置 18 { 19 int l=1,r=n; 20 while(l<r) 21 { 22 int mid=(l+r)/2;//二分位置 23 int cnt=mid-find_sum(mid);//看一下当前位置前面已经有多少个数了 24 if(cnt<a[i])l=mid+1;//因为如果有重复的话是放在前面,所以是小于号 25 else r=mid; 26 } 27 a[i]=l;change_sum(a[i]); 28 } 29 memset(s,0,sizeof(s)); 30 int ans=0; 31 for(int i=1;i<=n;i++) 32 { 33 int cnt=find_max(a[i])+1;//算上自己就要加 1 34 change_max(a[i],cnt); 35 ans=max(ans,cnt); 36 printf("%d\n",ans); 37 } 38 return 0; 39 }