牛客-小魂和他的数列-树状数组

题目描述:

一天,小魂正和一个数列玩得不亦乐乎。
小魂的数列一共有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的范围,可以遍历!
牛客-小魂和他的数列-树状数组_第1张图片举个例子:
n=5,k=3。
考虑暴力的做法:
子序列长度为1,则每个数作为尾数,贡献都是1
子序列长度为i,第j个数作为尾数的贡献=i-1轮满足以下条件的数的贡献之和
条件1、位置在j之前
条件2、数值小于h[j]

暴力求和的时间复杂度为n2,需要降低复杂度。
控制遍历的顺序满足条件1,用离散后的权值树状数组满足条件2log级求贡献和。

详见代码!

代码如下:

#include
#include
#include
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;
}

你可能感兴趣的:(树状数组)