小魂和他的数列(dp+树状数组优化)
鏈接:https://ac.nowcoder.com/acm/contest/3566/C
來(lái)源:牛客網(wǎng)
Sometimes, even if you know how something’s going to end, that doesn’t mean you can’t enjoy the ride.
有時(shí)候,即使你知道了故事的結(jié)局,也不代表你不可以享受它的過(guò)程。
小魂和他的數(shù)列
時(shí)間限制:C/C++ 2秒,其他語(yǔ)言4秒 空間限制:C/C++ 131072K,其他語(yǔ)言262144K 64bit IO Format: %lld題目描述
一天,小魂正和一個(gè)數(shù)列玩得不亦樂(lè)乎。
小魂的數(shù)列一共有n個(gè)元素,第i個(gè)數(shù)為Ai。
他發(fā)現(xiàn),這個(gè)數(shù)列的一些子序列中的元素是嚴(yán)格遞增的。
他想知道,這個(gè)數(shù)列一共有多少個(gè)長(zhǎng)度為K的子序列是嚴(yán)格遞增的。
請(qǐng)你幫幫他,答案對(duì)998244353取模。
對(duì)于100%的數(shù)據(jù),1≤ n ≤ 500,000,2≤ K ≤ 10,1≤ Ai ≤ 10^9。
輸入描述:
第一行包含兩個(gè)整數(shù)n,K,表示數(shù)列元素的個(gè)數(shù)和子序列的長(zhǎng)度。
第二行包含n個(gè)整數(shù),表示小魂的數(shù)列。
輸出描述:
一行一個(gè)整數(shù),表示長(zhǎng)度為K的嚴(yán)格遞增子序列的個(gè)數(shù)對(duì)998244353取模的值。
示例1
輸入
復(fù)制
輸出
2說(shuō)明
兩個(gè)子序列分別是2 3 3 5 1和2 3 3 5 1。
思路:
確定狀態(tài):
dp[i][j]:以i結(jié)尾長(zhǎng)度為j的嚴(yán)格上升子序列的個(gè)數(shù)。
那么有狀態(tài)轉(zhuǎn)移方程:
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]
根據(jù)狀態(tài)轉(zhuǎn)移方程可以寫(xiě)出:
時(shí)間復(fù)雜度: O(k*n^2)
很明顯時(shí)間復(fù)雜度不達(dá)標(biāo)。
此時(shí)想優(yōu)化:
上面第二層循環(huán)(k < i)就是求前綴和,K最大為10,
所以可以利用樹(shù)狀數(shù)組來(lái)優(yōu)化,開(kāi)K棵樹(shù)狀數(shù)組,
每個(gè)樹(shù)狀組數(shù)組存長(zhǎng)度為j的嚴(yán)格上升子序列的個(gè)數(shù)。
即:
時(shí)間復(fù)雜度: O(k*nlogn)
此時(shí)已經(jīng)可以在規(guī)定的復(fù)雜度里求出結(jié)果。
對(duì)于每一個(gè)數(shù)插入樹(shù)狀數(shù)組前的處理:
第一種方式是直接排序(特別注意排序規(guī)則),不去重,具體見(jiàn)下面的code1
第二種是離散化(a[i]比較大,可能出現(xiàn)很多重復(fù)的值)+去重處理,具體見(jiàn)下面的code2
AC代碼:
個(gè)人覺(jué)得code1更好理解
code1:
/*dp[i][j]:以i結(jié)尾長(zhǎng)度為j的嚴(yán)格上升子序列的個(gè)數(shù)。狀態(tài)轉(zhuǎn)移方程:dp[i][j] +=(a[j]<a[i])*dp[i-1][j-1];狀態(tài)轉(zhuǎn)移方程有約束條件:a[j]<a[i] && j<i;對(duì)于沒(méi)有去重,這里排完序(a[j]<a[i]--->從小到大排序),相同大小的(沒(méi)有特判,程序會(huì)統(tǒng)一當(dāng)作a[j]<a[i]處理),只有破壞j<i這個(gè)條件才不會(huì)統(tǒng)計(jì)錯(cuò)誤(多統(tǒng)計(jì)),所以讓相同大小的按index大的優(yōu)先排序。*/ #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;//按輸入的順序插入樹(shù)狀數(shù)組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;//序列長(zhǎng)度達(dá)不到j(luò)int 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), 就要知道插入那個(gè)數(shù)它排第幾 (low_bound(aim_arr_begin,aim_arr_end,x):返回im_arr中第一個(gè)大于等于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());//確定每個(gè)數(shù)的大小名次for(int i = 1; i <= n; i++){//這里要注意:+1,要不然下面(a[i]-1)會(huì)數(shù)組越界,而且樹(shù)狀數(shù)組也是從1開(kāi)始a[i] = lower_bound(vec.begin(),vec.end(),a[i])-vec.begin()+1;}int ans = 0;//這里按每個(gè)數(shù)的名次插入樹(shù)狀數(shù)組即可求前綴和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; }總結(jié)
以上是生活随笔為你收集整理的小魂和他的数列(dp+树状数组优化)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 小琛和他的学校(dfs)
- 下一篇: 利用STL离散化处理数据(unique)