BZOJ 3173[Tjoi2013]最长上升子序列(树状数组)

题目链接:http://www.lydsy.com/JudgeOnline/problem.php?id=3173

题解:话说这道题好像是用splay还是treap来模拟啊,可是我完全不会这两种算法啊。。。无奈,只能去学习了另一种 巧妙的办法。。。。

1.首先我们先得到最终队列的样子,第i个位置的数字为*i;

2.按照队列的顺序,i递增,我们用f[*i]来表示当插入*i这个数后,以*i为结尾的最长上升子序列的长度,用k[*i]来储存当前这个数未更新前f[1~*i]中的最大值,我们都知道,平常更新答案是f[i]=max(f[j]+1)即当j<i且*j<*i;那么因为这道题符合一个性质,就是我们是按数字的最终位置一个个插入的,所以我们保证只要f[j]不为0(j<i),那么j这个数字一定在i之前就已经做过了(if (f[j]>0) *j<*i);所以f[*i]一定等于k[*i]+1;

3.*i这个数字最后的答案为max(f[1~*i]);(因为题目是按数字的大小一个个插入的,所以最优解的结尾数字一定小于等于*i,毕竟其他数字都还没插入。。)

为了降低时间复杂度,我们用树状数组来维护k数组,复杂度为O(nlogn);

然后为了方便得出最终的队列的顺序,我学习了如何使用vector,当然,这个时间就变得慢多了。。。

#include<cstdio>
#include<cstring>
#include<iostream>
#include<vector>
using namespace std;
int k[2000000],n,p[2000000],f[2000000];
int lowbit(int x)
{
  return (x&-x);
}
int query(int x)
{
  int tot=0;
  while (x>0)
  {
      tot=max(tot,k[x]);
      x-=lowbit(x);
  }
  return tot;
}
void update(int x,int v)
{
    while (x<n)
    {
      k[x]=max(k[x],v);
      x+=lowbit(x);
    }
}
int main()
{
  scanf("%d",&n);
  int w;
  vector<int>a;
  for (int i=1;i<=n;i++)
  {
      scanf("%d",&w); 
      a.insert(a.begin()+w,i);//往vector里面插入
  }
  for (vector<int>::iterator i=a.begin();i!=a.end();i++)
   {
//*i表示在a的里面的第i个元素的值
//查询max(f[1~*i]);
        f[*i]=query(*i)+1;
//用树状数组来维护k数组
        update(*i,f[*i]);
   }
  for (int i=1;i<=n;i++) 
  {
   f[i]=max(f[i-1],f[i]);
//得出最后的答案
   printf("%d
",f[i]);
  }
  return 0;
}
原文地址:https://www.cnblogs.com/2014nhc/p/6509596.html