牛客-小魂和他的數列-樹狀數組

題目描述:

一天,小魂正和一個數列玩得不亦樂乎。
小魂的數列一共有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;
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章