• 【二分】【动态规划】Gym


    求最长上升子序列方案数。

    转载自:http://blog.csdn.net/u013445530/article/details/47958617,如造成不便,请博主联系我。

    数组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示例 






    Output示例 
    2

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

    先开始我想到的是o(n^2)的做法,很容易理解

    #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
    ",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
    ", sum[Max].back());
        }
    
    
    
    
        return 0;
    }
  • 相关阅读:
    redis之Scan
    redis之GeoHash
    redis之漏斗限流
    redis之布隆过滤器
    redis之HyperLogLog
    redis位图
    redis延迟队列
    redis分布式锁
    如何安装redis
    社区首款 OAM 可视化平台发布!关注点分离、用户友好、上手难度低
  • 原文地址:https://www.cnblogs.com/autsky-jadek/p/8343052.html
Copyright © 2020-2023  润新知