題目描述:
一天,小魂正和一個數列玩得不亦樂乎。
小魂的數列一共有n個元素,第i個數爲Ai。
他發現,這個數列的一些子序列中的元素是嚴格遞增的。
他想知道,這個數列一共有多少個長度爲K的子序列是嚴格遞增的。
請你幫幫他,答案對998244353取模。
對於100%的數據,1≤ n ≤ 500,000,2≤ K ≤ 10,1≤ Ai ≤ 109。
輸入描述:
第一行包含兩個整數n,K,表示數列元素的個數和子序列的長度。
第二行包含n個整數,表示小魂的數列。
輸出描述:
一行一個整數,表示長度爲K的嚴格遞增子序列的個數對998244353取模的值。
輸入樣例:
5 3
2 3 3 5 1
輸出樣例:
2
核心思想:
注意k的範圍,可以遍歷!
舉個例子:
n=5,k=3。
考慮暴力的做法:
子序列長度爲1,則每個數作爲尾數,貢獻都是1
子序列長度爲i,第j個數作爲尾數的貢獻=i-1輪滿足以下條件的數的貢獻之和:
條件1、位置在j之前
條件2、數值小於h[j]
暴力求和的時間複雜度爲n2,需要降低複雜度。
控制遍歷的順序來滿足條件1,用離散後的權值樹狀數組來滿足條件2log級求貢獻和。
詳見代碼!
代碼如下:
#include<cstdio>
#include<iostream>
#include<algorithm>
using namespace std;
typedef long long ll;
const int N=5e5+20,K=11;
const ll mo=998244353;
int n,cb,b[N],g[N],h[N];//b用於離散,g用於離散映射,h存原數組
ll c[K][N];
int lowbit(int x)
{
return x&(-x);
}
void update(int u,int k,ll x)
{
while(k<=cb)
{
c[u][k]=(c[u][k]+x)%mo;
k+=lowbit(k);
}
return;
}
ll getsum(int u,int k)
{
ll ans=0;
while(k>0)
{
ans=(ans+c[u][k])%mo;
k-=lowbit(k);
}
return ans;
}
int getid(int x)//用於離散
{
return lower_bound(b,b+cb,x)-b+1;
}
int main()
{
int k;
cin>>n>>k;
for(int i=1;i<=n;i++)
{
scanf("%d",&h[i]);
b[i-1]=h[i];
}
sort(b,b+n);
cb=unique(b,b+n)-b;
for(int i=1;i<=n;i++)
{
g[i]=getid(h[i]);
update(0,g[i],1);
for(int z=1;z<k;z++)
{
ll t=getsum(z-1,g[i]-1);
update(z,g[i],t);
}
}
cout<<getsum(k-1,cb)<<endl;
return 0;
}