Longest Increasing Subsequence

Time Limit: 2000/1000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)
Total Submission(s): 189    Accepted Submission(s): 45


Problem Description
Bobo has a sequence  a1,a2,,an.
Let  f(x) be the length of longest *strictly* increasing subsequence after replacing all the occurrence of  0 with  x.
He would like to find  ni=1if(i).

Note that the length of longest strictly increasing subsequence of sequence  s1,s2,,sm is the largest  k
such that there exists  1i1<i2<<ikm satisfying  si1<si2<<sik.
 

Input
The input consists of several test cases and is terminated by end-of-file.

The first line of each test case contains an integer  n.
The second line contains  n integers  a1,a2,,an.
 

Output
For each test case, print an integer which denotes the result.

## Constraint

1n105
0ain
* The sum of  n does not exceed  250,000.
 

Sample Input
2 1 1 3 1 0 3 6 4 0 6 1 0 3
 

Sample Output
3 14 49
 

Source
 

 题意:

一个n个数的数列,f(i)表示将数列中所有的0替换成i之后数列的最长严格上升子序列的长度,求i*f(i)的和。


题解:

设不考虑0时原序列的LIS为L,设a[i],b[i]分别表示以点i结束和开始的LIS的长度,c[i]为原数列。

对每一个i,如果存在 i 的下一个0后面的j,满足a[i]+b[j]=L,那么替换掉0之后,LIS的长度为L+1,可以将0替换的范围时[c[i]+1,c[j]-1],这是显而易见的。

具体实现时,我用O(n)的实现方法,虽然题目的数据没有卡其他方法,以防万一嘛。

首先,可以想到对于每一个i,要找的j一定在i的后方,这使我想到可以从后往前处理,用一个数组f[i]保存最长上升子序列长度为i的起始位置的数的最大值,因为这是所有满足要求的j的对应区间[c[i]+1,c[j]-1]的并。这样的话,每处理一个位置i,就更新f[i].

但还有一个问题,就是i与j之间必须至少要有一个0,我们如果边求区间,边更新f数组的话显然是错的(想一想为什么),怎么办?我用分段处理,每次处理相邻两个0之间的位置,处理完后再重新扫一遍,更新f数组。

还有就是区间的处理方法,当[a,b]为可取区间时,另开一个数组d,将d[a]++,d[b+1]--,然后到最后求前缀和,若d[i]为0,说明替换为数字i后LIS长度为L,反之为L+1。此题就完全解决了。

代码:

#include<bits/stdc++.h>
#define N 100010
#define LL long long
using namespace std;
const LL mod=1e9+7;
int a[N],b[N],c[N],f[N],d[N];

int main()
{
    int n;
    while (~scanf("%d",&n))
    {
        for (int i=0;i<=n;i++)a[i]=b[i]=d[i]=f[i]=0;
        for (int i=1;i<=n;i++) scanf("%d",&c[i]);
        int k=1;
        while (c[k]==0 && k<=n) k++;
        f[1]=c[k];int t=0;a[k]=1;
        if (k<=n)t=1;
        for (int i=k+1;i<=n;i++) if (c[i])
        {
            int j=lower_bound(f+1,f+t+1,c[i])-f;
            f[j]=c[i];
            a[i]=j;                 //end
            if (j==t+1) t++;
        }

        k=n;
        while (c[k]==0 && k) k--;
        f[1]=-c[k];t=0; b[k]=1;
        if (k>0)t=1;
        for (int i=k-1;i>0;i--) if (c[i])
        {
            int j=lower_bound(f+1,f+t+1,-c[i])-f;
            f[j]=-c[i];
            b[i]=j;                 //begin
            if (j==t+1) t++;
        }
        for (int i=0;i<=n;i++)f[i]=0;
        k=n;
        //f[0]=n+1;
        while(k>0)
        {
            int y=k;
            for (;k>0;k--)
            {
                if (!c[k]){f[0]=n+1;break;}
                int u=t-a[k];
                if (f[u]-1>c[k])
                {
                    d[c[k]+1]++;
                    d[f[u]]--;
                }
            }
            for (int i=y;i>k;i--) if (f[b[i]]<c[i]) f[b[i]]=c[i];
            if (f[t]-1>c[k] && k)
            {
                d[c[k]+1]++;
                d[f[t]]--;
            }
            k--;
        }
        for (int i=1;i<=n;i++) d[i]+=d[i-1];  
        LL ans=0;
        for (int i=1;i<=n;i++)if (!d[i])ans+=(LL) i*(t);else ans+=(LL) i*(t+1);
        printf("%I64d\n",ans);
    }
}