bzoj3173: [Tjoi2013]最长上升子序列(树状数组+二分倒推)

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 }

 

posted @ 2018-03-10 16:08  CHerish_OI  阅读(188)  评论(0编辑  收藏  举报