【二分】【動態規劃】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; }
相關文章
相關標籤/搜索