Longest Increasing Subsequence HDU - 6284 (LIS+区间分解)

思路:因为改变的数是同一个,所以最后对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);
    }
}

猜你喜欢

转载自blog.csdn.net/qq_40774175/article/details/82895774