【二分】【动态规划】Gym - 101156E - Longest Increasing Subsequences

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

转载自: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的个数呢?ui

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

#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,都说用线段树或者树状数组搞,好吧,没搞出来。.net

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

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

add 2:get

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

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; }
相关文章
相关标签/搜索