数组A包括N个整数(可能包括同样的值)。设S为A的子序列且S中的元素是递增的,则S为A的递增子序列。假设S的长度是全部递增子序列中最长的,则称S为A的最长递增子序列(LIS)。A的LIS可能有非常多个。比如A为:{1 3 2 0 4},1 3 4,1 2 4均为A的LIS。

给出数组A,求A的LIS有多少个。因为数量非常大,输出Mod 1000000007的结果就可以。同样的数字在不同的位置,算作不同的。比如 {1 1 2} 答案为2。
Input
第1行:1个数N,表示数组的长度。(1 <= N <= 50000)
第2 - N + 1行:每行1个数A[i],表示数组的元素(0 <= A[i] <= 10^9)
Output
输出最长递增子序列的数量Mod 1000000007。


Input演示样例
5
1
3
2
0
4
Output演示样例
2

必须用nlogn算法,否则超时,那么我们怎样计算LIS的个数呢?

先開始我想到的是o(n^2)的做法,非常easy理解

#include <iostream>
#include <algorithm>
#include <cstdio>
using namespace std;
const int M = 500000+100;

int a[M];
int c[M];
int dp[M];
long long cent[M];

int INF = 1e9 + 1000;
const int mod =1000000007;

int input()
{
    int ans=0;
    char a;
    while((a=getchar())<'0'||a>'9');
    ans=a-'0';
    while((a=getchar())>='0'&&a<='9')
    {
        ans=ans*10+a-'0';
    }
    return ans;
}

int main()
{
    int n;
    #ifdef xxz
     freopen("in.txt","r",stdin);
    #endif // xxz

    while(~scanf("%d",&n))
    {
        for(int i = 0; i < n; i++) a[i] = input() , cent[i] = 1;
        int Max = 0;

        fill(dp,dp+n,0);
        long long ans = 0;
        for(int i = 0; i < n; i++)
        {
            dp[i] = 1;
            for(int j = 0; j < i; j++)
            {
                if(a[j] < a[i])
                {
                    if(dp[i] < dp[j] + 1)
                    {
                        dp[i] = dp[j] + 1;
                        cent[i] = cent[j];
                    }
                    else if(dp[i] == dp[j] + 1)  cent[i] = (cent[i] +cent[j])%mod;
                }
            }

            Max = max(Max,dp[i]);

        }

        for(int i = 0; i < n; i++)
        {
            if(dp[i] == Max) ans = (ans + cent[i]) % mod;
        }

        printf("%d\n",ans%mod);
    }

    return 0;
}

然后从网上搜nlogn的算法没搜到。然后问了好多大神。九爷,鸟神,rabbit,都说用线段树或者树状数组搞,好吧,没搞出来。

然后问tyh,他搜到了一篇国外高手写的思路。看完以后直接转换为代码
二分+前缀和,orz….膜拜田博士……..
果然搜索姿势要正确呀
思路地址:
http://stackoverflow.com/questions/22923646/number-of-all-longest-increasing-subsequences

我用中文解释下:
就是取二元组(i,j),i表示以i元素结尾的序列,j表示方案数
比方:
add 1
len1: (1,1);

add 2:

len1(1,1);
len2(2,1);

add 5
len1 (1,1);
len2 (2,1);
len3 (5,1);

add 4
len1 (1,1);
len2 (2,1);
len3 (5,1) (4,1);
……

我们能够找到规律。就是没一行j都是从达到小降低
新插入一个数,我们先找它应该处于哪一行,用
就是用LIS的nlogn算法找。它的方案数就等于它上一行比这个数小的全部方案和


#include <iostream>
#include <algorithm>
#include <cstdio>
#include <cstring>
#include <cmath>
#include <cstdlib>
using namespace std;

typedef long long LL;
const int MOD = 1e9 + 7;
const int INF = 0x7fffffff;
const int N = 50000 + 10;

vector <int> val[N];        // val[i]: 最大长度为i+1的序列的最后一个元素组成的序列
vector <int> sum[N];        // sum[i]: 相应val中每一个序列数量的组成的前缀和。
vector <int> last(N, INF);  // last[i]: val[i].back()

int input()
{
    int ans=0;
    char a;
    while((a=getchar())<'0'||a>'9');
    ans=a-'0';
    while((a=getchar())>='0'&&a<='9')
    {
        ans=ans*10+a-'0';
    }
    return ans;
}

void add(int x, int len, int v)
{
    val[len].push_back(x); 
    if(sum[len].size() == 0)
    {
        sum[len].push_back(v);
    }
    else
    {
        sum[len].push_back((sum[len].back() + v) % MOD);
    }
    last[len] = x;
}

int main()
{

    int n, x;
    while (scanf("%d", &n) != EOF)
    {
        int Max = 0;
        for(int i = 0; i < n; i++)
        {
            x = input();
            int len = lower_bound(last.begin(), last.end(), x) - last.begin();
            Max = max(Max, len);
            if(len == 0)
            {
                add(x, len, 1);
            }
            else
            {
                int pos = upper_bound(val[len - 1].begin(), val[len - 1].end(), x,greater<int>() ) - val[len - 1].begin();
                int cnt;
                if(pos == 0)
                {
                    cnt = sum[len - 1].back();
                }
                else
                {
                    cnt = (sum[len - 1].back() - sum[len - 1][pos - 1] + MOD) % MOD;
                }
                add(x, len, cnt);
            }
        }
        printf("%d\n", sum[Max].back());
    }




    return 0;
}
posted on 2017-07-30 19:51  lxjshuju  阅读(290)  评论(0编辑  收藏  举报