bzoj3173 最长上升子序列 树状数组
链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3173
题意:向序列中动态插入$1~n$排列元素,求出插入每个元素后最长上升子序列长度。
如Claris所言,面对这种数据结构,必有高论。如果只想着数据结构,我们可以通过平衡树动态维护序列,同时使用树状数组计算最长上升子序列。
但是我们不是猩猩不是数据结构狂人,我们毕竟还是要本着能不上树就不上树能少用数据结构就少用的原则来设计算法的。
重新考虑这个题目。本题没有要求强制在线,于是我们把整个操作倒过来看,于是问题就变成了不停地删去序列中某个元素后求出当前最长上升子序列长度。
然后,不要忘了一个重要条件:这个东西是从小到大插入的。
从小到大插入,意味着插入一个数之后最长上升子序列发生变化的唯一可能就是以这个数结尾。
那么我们就维护一下删掉每个数之前以这个数结尾的最长上升子序列就好了,最后输出答案时,将这个与前一个比较,短就直接将当前这个数赋值成为上一个。
说得简单……但是这道题真的是一个树状数组综合运用……具体看代码吧……
1 #include<iostream> 2 #include<cstdio> 3 #include<algorithm> 4 #include<cstring> 5 using namespace std; 6 int lowbit(int x) 7 { 8 return x&(-x); 9 } 10 const int maxn=100005; 11 int C[maxn],n; 12 void Modify(int pos,int val) 13 { 14 for(;pos<=n;pos+=lowbit(pos))C[pos]+=val; 15 } 16 void Maximum(int pos,int val) 17 { 18 for(;pos<=n;pos+=lowbit(pos))C[pos]=max(C[pos],val); 19 } 20 int Query_kth(int k) 21 { 22 int cnt=0,ans=0; 23 for(int i=17;~i;i--) 24 { 25 ans+=(1<<i); 26 if(ans>=n||cnt+C[ans]>=k)ans-=(1<<i); 27 else cnt+=C[ans]; 28 } 29 return ans+1; 30 } 31 int Query_max(int pos) 32 { 33 int res=0; 34 for(;pos;pos-=lowbit(pos))res=max(res,C[pos]); 35 return res; 36 } 37 int a[maxn],b[maxn],f[maxn]; 38 int haha() 39 { 40 scanf("%d",&n); 41 for(int i=1;i<=n;i++) 42 { 43 scanf("%d",&a[i]);C[i]++; 44 if((i+lowbit(i))<=n)C[i+lowbit(i)]+=C[i]; 45 } 46 int tmp; 47 for(int i=n;i;i--)b[tmp=Query_kth(a[i]+1)]=i,Modify(tmp,-1); 48 memset(C,0,sizeof(C)); 49 for(int i=1;i<=n;i++)f[b[i]]=Query_max(b[i])+1,Maximum(b[i],f[b[i]]); 50 for(int i=1;i<=n;i++)printf("%d\n",f[i]<f[i-1]?(f[i]=f[i-1]):f[i]); 51 } 52 int sb=haha(); 53 int main(){;}
只要是活着的东西,就算是神我也杀给你看。