日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

小魂和他的数列(dp+树状数组优化)

發布時間:2024/9/3 编程问答 40 豆豆
生活随笔 收集整理的這篇文章主要介紹了 小魂和他的数列(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

輸入
復制

5 3 2 3 3 5 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=1i?1?(a[k]<a[i])?dp[k][j?1]
根據狀態轉移方程可以寫出:

for(int i = 1; i <= n; i++) {for(int k = 1; k < i; k++){for(int j = 1; j <= K; j++){dp[i][j] += (a[k ] < a[i]) * dp[k][j-1];}} }

時間復雜度: O(k*n^2)
很明顯時間復雜度不達標。
此時想優化:
上面第二層循環(k < i)就是求前綴和,K最大為10,
所以可以利用樹狀數組來優化,開K棵樹狀數組,
每個樹狀組數組存長度為j的嚴格上升子序列的個數。
即:

for(int i = 1; i <= n; i++) {for(int j = 1; j <= K; j++){dp[i][j] += query(id,pos);} }

時間復雜度: 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+树状数组优化)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。