题目描述:
一天,小魂正和一个数列玩得不亦乐乎。
小魂的数列一共有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;
}