2019hdu暑假多校训练赛第六场1002 Nonsense Time hdu6635(求最长上升子序列并记录元素,二分贪心)

题目链接:

http://acm.hdu.edu.cn/showproblem.php?pid=6635

题意:给一个长度为N的序列,由1到N这N个正整数组成【即1到N各出现一次】,序列元素起始状态为冻结【不可用】,然后给出b序列代表解冻顺序【若b[1]=2,则a[2]解冻】,询问对于k从1到N,解冻前k个b[i]对应的元素a [b [ i ] ],解冻元素中最长上升子序列的长度是多少。T<=3         N<=5e4

思路:考虑时间倒流,看作一个完整的排列按照一定顺序依次删除每个数,然后每次需要计算 LIS 的长度。 首先在 O(nlogn) 的时间内求出 LIS,并找到一个 LIS。当删除 x 时,如果 x 不在之前找 到的那个 LIS 中,那么显然 LIS 的长度是不会变化的,否则暴力重新计算出新的 LIS 即可。 因为数据随机,因此 LIS 的期望长度是 O(√n),删除的 x 位于 LIS 中的概率是 1 √n,也就 是说期望删除 O(√n) 个数才会修改 LIS,那么 LIS 变化的次数不会很多。 期望时间复杂度为 O(n√nlogn)。

【题解给出的标程用了很多个数组看着特别头疼(看不懂,所以我想自己找一个比较简单的做法,想了一晚上(太菜,终于想到了! ^ ^】

先给出AC代码:

#include
#include
#include
#include
using namespace std;
typedef long long LL;
const int maxx=5e4+10;
const int inf=0x7ffffff;
int t,n,pos,tot;
int a[maxx];
int b[maxx];
int f[maxx];
int pre[maxx];
int m[maxx];
int ans[maxx];
int xx[maxx];   ///离散化
inline void build(int w)
{
        pos=0;
        f[0]=a[0];
        for(int i=1; i<=n; i++)
        {
            if(a[i]==-1)
                continue;
            if(a[i]>f[pos])
            {
                f[++pos] = a[i];
                pre[a[i]]=f[pos-1];
            }
            else
            {
                tot=lower_bound(f,f+pos+1,a[i])-f;
                pre[a[i]]=f[tot-1];
                f[tot]=a[i];
            }
        }
        for(int i=f[pos],k=pos; k; k--,i=pre[i])
            m[i]=w;
}
int main()
{
    scanf("%d",&t);
    while(t--)
    {
        scanf("%d",&n);
        memset(m,-1,sizeof(m));
        for(int i=1; i<=n; i++)
        {
            scanf("%d",&a[i]);
            xx[i]=a[i];
        }
        sort(xx+1,xx+n+1);
        for(int i=1;i<=n;i++) a[i]=lower_bound(xx+1,xx+1+n,a[i])-xx;
        int id=0;
        for(int i=1;i<=n;i++)
            scanf("%d",&b[i]);
        build(0);
        ans[n]=pos;
       for(int i=n;i>1;i--)
       {
           if(m[a[b[i]]]!=id)
            a[b[i]]=-1;
           else
           {
               a[b[i]]=-1;
               build(++id);
           }
          ans[i-1]=pos;
       }
       for(int i=1;i<=n;i++)
        printf("%d%c",ans[i]," \n"[i==n]);
    }
}

我对于贪心二分找最长上升子序列的方法做了一点添加,加了一个pre数组,记录每个元素它前面比它小的最近的元素是哪个,然后以f 数组的最后一个元素为循环起点,一直往前pre  pos个位置,就得到最长上升子序列中的元素了,标记的时候用个小技巧就不用清空了。

你可能感兴趣的:(最长上升子序列)