1. 程式人生 > >Longest Increasing Subsequence HDU

Longest Increasing Subsequence HDU

思路:因為改變的數是同一個,所以最後對LIS的貢獻最多隻能是1,所以可以先求出最長上升子序列長度,然後每個改變的數,考慮它對LIS的加成是1還是0.

設a[i]表示已第i項結束的最長上升子序列長度,b[i]表示以第i項為起點的最長上升子序列長度。

考慮在0的左邊找一個a[i],在0的右邊找一個b[j](並且c[i]<c[j]),如果a[i]+b[j]==L,那麼將這個0改成其他數字,就有可能使得LIS增加。

下面考慮如何找這樣的對數。

首先找這樣的對數一定是在0的兩邊開始找的,如果在同一邊,這時沒有意義的。

設f[i]表示長度為i的,起點最大的c[i]。找最大的開始點是因為,假設在0的左邊有a[i],在0的右邊有多個b[j]滿足a[i]+b[j]==L,這時我們只有找一個最大的c[j]就行,因為它可以包含其他的。

下面介紹O(n)的方法求出滿足的。

設一個區間d[] 如果d[i]>0表示i這個值有效,假設我們找到一個區間[a,b]表示a到b之間的數都可以滿足+1,那麼我們可以這樣考慮,讓d[a+1]++,d[b]--,表示在a+1以前都可以滿足,而超出b以後,就不行,所以立刻-1.這樣我們求一個求字首和就清楚了,如果大於0,表示滿足,==0,表示不滿足。

這個處理技巧非常巧妙。具體實現看程式碼和註釋吧。

程式碼:

#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];//更新f的最大值
            if (f[t]-1>c[k] && k) //如果找到一個,以它為起點的LIS長度==L,那麼在這個0的位置放上1到f[t],必然可以增加LIS.
            {
                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);
    }
}