小魂和他的数列(dp+树状数组优化)
鏈接:https://ac.nowcoder.com/acm/contest/3566/C
來源:牛客網
Sometimes, even if you know how something’s going to end, that doesn’t mean you can’t enjoy the ride.
有時候,即使你知道了故事的結局,也不代表你不可以享受它的過程。
小魂和他的數列
時間限制:C/C++ 2秒,其他語言4秒 空間限制:C/C++ 131072K,其他語言262144K 64bit IO Format: %lld題目描述
一天,小魂正和一個數列玩得不亦樂乎。
小魂的數列一共有n個元素,第i個數為Ai。
他發現,這個數列的一些子序列中的元素是嚴格遞增的。
他想知道,這個數列一共有多少個長度為K的子序列是嚴格遞增的。
請你幫幫他,答案對998244353取模。
對于100%的數據,1≤ n ≤ 500,000,2≤ K ≤ 10,1≤ Ai ≤ 10^9。
輸入描述:
第一行包含兩個整數n,K,表示數列元素的個數和子序列的長度。
第二行包含n個整數,表示小魂的數列。
輸出描述:
一行一個整數,表示長度為K的嚴格遞增子序列的個數對998244353取模的值。
示例1
輸入
復制
輸出
2說明
兩個子序列分別是2 3 3 5 1和2 3 3 5 1。
思路:
確定狀態:
dp[i][j]:以i結尾長度為j的嚴格上升子序列的個數。
那么有狀態轉移方程:
dp[i][j]=∑k=1i?1(a[k]<a[i])?dp[k][j?1]dp[i][j] = \sum_{k=1}^{i-1} (a[k] < a[i]) * dp[k][j-1] dp[i][j]=k=1∑i?1?(a[k]<a[i])?dp[k][j?1]
根據狀態轉移方程可以寫出:
時間復雜度: O(k*n^2)
很明顯時間復雜度不達標。
此時想優化:
上面第二層循環(k < i)就是求前綴和,K最大為10,
所以可以利用樹狀數組來優化,開K棵樹狀數組,
每個樹狀組數組存長度為j的嚴格上升子序列的個數。
即:
時間復雜度: O(k*nlogn)
此時已經可以在規定的復雜度里求出結果。
對于每一個數插入樹狀數組前的處理:
第一種方式是直接排序(特別注意排序規則),不去重,具體見下面的code1
第二種是離散化(a[i]比較大,可能出現很多重復的值)+去重處理,具體見下面的code2
AC代碼:
個人覺得code1更好理解
code1:
/*dp[i][j]:以i結尾長度為j的嚴格上升子序列的個數。狀態轉移方程:dp[i][j] +=(a[j]<a[i])*dp[i-1][j-1];狀態轉移方程有約束條件:a[j]<a[i] && j<i;對于沒有去重,這里排完序(a[j]<a[i]--->從小到大排序),相同大小的(沒有特判,程序會統一當作a[j]<a[i]處理),只有破壞j<i這個條件才不會統計錯誤(多統計),所以讓相同大小的按index大的優先排序。*/ #include <bits/stdc++.h> using namespace std; const int N = 5e5+5; const int mod = 998244353; struct Node {int x;int pos;bool operator<(const Node& n)const{if(x == n.x){return pos > n.pos;}return x < n.x;} } a[N]; int n,k; int sum[12][N]; inline int lowbit(int x) {return x&-x; } void add(int id,int pos,int v) {while(pos <= n){sum[id][pos] = (sum[id][pos] + v) % mod;pos += lowbit(pos);} } int query(int id,int pos) {int res = 0;while(pos){res = (res + sum[id][pos]) % mod;pos -= lowbit(pos);}return res; } int main() {scanf("%d%d",&n,&k);for(int i = 1; i <= n; i++){scanf("%d",&a[i].x);a[i].pos = i;}sort(a+1,a+n+1);int ans = 0;//按輸入的順序插入樹狀數組for(int i = 1; i <= n; i++){add(1,a[i].pos,1);for(int j = 2; j <= k; j++){if(j > a[i].pos) break;//序列長度達不到jint v = query(j-1,a[i].pos-1);add(j,a[i].pos,v);if(j == k) ans = (ans + v) % mod;}}printf("%d\n",ans);return 0; }code2:
/* 如果去重(sort+unique+erase), 就要知道插入那個數它排第幾 (low_bound(aim_arr_begin,aim_arr_end,x):返回im_arr中第一個大于等于x的地址) */#include <bits/stdc++.h> using namespace std; const int N = 5e5+5; const int mod = 998244353; int n,k; int a[N]; vector<int>vec; int sum[12][N]; inline int lowbit(int x) {return x&-x; } void add(int id,int pos,int v) {while(pos <= n){sum[id][pos] = (sum[id][pos] + v) % mod;pos += lowbit(pos);} } int query(int id,int pos) {int res = 0;while(pos){res = (res + sum[id][pos]) % mod;pos -= lowbit(pos);}return res; } int main() {scanf("%d%d",&n,&k);for(int i = 1; i <= n; i++){scanf("%d",&a[i]);vec.push_back(a[i]);}//離散化處理sort(vec.begin(),vec.end());vec.erase(unique(vec.begin(),vec.end()),vec.end());//確定每個數的大小名次for(int i = 1; i <= n; i++){//這里要注意:+1,要不然下面(a[i]-1)會數組越界,而且樹狀數組也是從1開始a[i] = lower_bound(vec.begin(),vec.end(),a[i])-vec.begin()+1;}int ans = 0;//這里按每個數的名次插入樹狀數組即可求前綴和for(int i = 1; i <= n; i++){add(1,a[i],1);for(int j = 2; j <= k; j++){int v = query(j-1,a[i]-1);add(j,a[i],v);if(j == k) ans = (ans + v) % mod;}}printf("%d\n",ans);return 0; }總結
以上是生活随笔為你收集整理的小魂和他的数列(dp+树状数组优化)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 小琛和他的学校(dfs)
- 下一篇: 利用STL离散化处理数据(unique)