The All-purpose Zero

简要题意: 

长度为n的数组,每个数字为S[i],$0$是一种很神奇的数字,你想要的,它都可以变!

问这个序列的最长上升子序列长度为多少?

分析: 

我们将除了‘0’以外的S[i],减去i之前出现的‘0’的个数,最后求得排除‘0’后的最长上升子序列长度,加上‘0’的个数,就是我们要求的答案。

在这里我不主要分析该做法的正确性,我们引入一个O(nlogn)的方法来求最长上升子序列。

LIS (点此看题)

贪心+二分

我们令f[i]表示长度为i的上升子序列,末尾的最小值。

这里我们因为贪心,所以子序列末尾越小越好。

我们按顺序枚举给出的数组S[i],然后对f[]二分查找除小于S[i]的最大的f[j],并用它将f[j+1]更新。

#include<bits/stdc++.h>
using namespace std;
#define re register int
#define LL long long
const int N=1e5+5;
int n, a[N], b[N], len;
signed main()
{
    scanf("%d",&n);
    for(re i=1;i<=n;++i) scanf("%d",&a[i]);
    for(re i=1;i<=n;++i)
    {
        if(a[i] > b[len]) b[++len] = a[i];
        else
        {
            int tp = lower_bound(b+1, b+1+len, a[i])-b;
            b[tp] = a[i];
        }
    }
    printf("%d", len);
}

上马

热身训练2 The All-purpose Zero_数组热身训练2 The All-purpose Zero_#define_02
#include<bits/stdc++.h>
using namespace std;
#define re register int
#define LL long long
const int N=1e5+5;
int n, a[N], b[N], num, len;
inline void work()
{
    num = len = 0;
    scanf("%d",&n);
    for(re i=1;i<=n;++i)
    {
        scanf("%d",&a[i]);
        if(a[i] == 0)
        {
            num ++;
            a[i] = 1e9;
        }
        else
        {
            a[i] -= num;
        }
    }
    b[0] = -1e9;
    for(re i=1;i<=n;++i)
    {
        if(a[i] == 1e9) continue;
        
        if(a[i] > b[len]) b[++len] = a[i];
        else
        {
            int tp = lower_bound(b+1, b+1+len, a[i])-b;
            b[tp] = a[i];
        }
    }
    printf("%d\n", len+num);
}
signed main()
{
    int T;
    scanf("%d",&T);
    for(re i=1;i<=T;++i) 
    {
        printf("Case #%d: ",i);
        work();
    }
    return 0;
}
View Code