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

歡迎訪(fǎng)問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

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

發(fā)布時(shí)間:2024/9/3 编程问答 35 豆豆
生活随笔 收集整理的這篇文章主要介紹了 小魂和他的数列(dp+树状数组优化) 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

鏈接: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ù)制

5 3 2 3 3 5 1

輸出

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=1i?1?(a[k]<a[i])?dp[k][j?1]
根據(jù)狀態(tài)轉(zhuǎn)移方程可以寫(xiě)出:

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];}} }

時(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ù)。
即:

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

時(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)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。