【NOIP1999普及组】导弹拦截的二分解法——关于LIS

版权声明:转载请注明原出处啦(虽然应该也没人转载): https://blog.csdn.net/hzk_cpp/article/details/82051886

题目:luogu1020.

第一问就是求一个最长不上升子序列,而第二问你再来一发最长上升子序列.

我们现在知道LIS问题有一个O(n^2)的算法,但是这个算法在这个问题中并不能拿到满分.

我们现在考虑第一个问题的优化,我们考虑到枚举到点i的时候,最长的长度为f[i],然后我们再记录一个值g[i],表示长度为i的子序列的结尾的最大值.

那么我们很容易发现,g[i]具有单调性,也就是g[i]会越来越小.

那我们是不是就可以二分来做了,我们直接每次转移的时候找到g[i]刚好比a[i]小或等于的i+1.

那么第二问翻一下也比较简单了吧.

代码如下:

#include<bits/stdc++.h>
  using namespace std;
#define Abigail inline void
const int N=100000;
const int INF=1<<31-1;
int n,a[N+9],f[N+9],g[N+9],ans1,ans2;
Abigail into(){
  while (~scanf("%d",&a[++n]));
}
Abigail work(){
  --n;
  int l,r,mid,now;
  f[1]=1;g[1]=a[1];now=1;g[0]=INF;
  for (int i=2;i<=n;i++){
    l=0;r=now;
    while (l+1<r){
      mid=l+r>>1;
      if (g[mid]>=a[i]) l=mid;
      else r=mid;
    }
    if (g[r]>=a[i]) mid=r;
    else mid=l;
    f[i]=mid+1;
    if (now<f[i]) now++,g[f[i]]=a[i];
    else g[f[i]]=max(g[f[i]],a[i]);
    ans1=max(ans1,f[i]);
  }
  f[1]=1;g[1]=a[1];now=1;g[0]=-INF;
  for (int i=2;i<=n;i++){
    l=0;r=now;
    while (l+1<r){
      mid=l+r>>1;
      if (g[mid]<a[i]) l=mid;
      else r=mid;
    }
    if (g[r]<a[i]) mid=r;
    else mid=l;
    f[i]=mid+1;
    if (now<f[i]) now++,g[f[i]]=a[i];
    else g[f[i]]=min(g[f[i]],a[i]);
    ans2=max(ans2,f[i]);
  }
}
Abigail outo(){
  printf("%d\n%d\n",ans1,ans2);
}
int main(){
  into();
  work();
  outo();
  return 0;
}

猜你喜欢

转载自blog.csdn.net/hzk_cpp/article/details/82051886