题解 CF1188C 【Array Beauty】
考虑对美丽值进行 \(DP\) 来统计答案,发现直接统计美丽值等于 \(v\) 的子序列方案数不好统计,不妨统计美丽值大于等于 \(v\) 的方案数。发现这样统计,美丽值为 \(v\) 的一个子序列在区间 \([1,v]\) 都会有贡献,所以这样的合法子序列的方案数即为答案。
先对 \(a\) 进行排序,由抽屉原理得美丽值的最大值为 \(\frac{a_n}{k-1}\)。枚举当前考虑的美丽值为 \(v\),设 \(f_{i,j}\) 为考虑了前 \(i\) 个数,选了 \(j\) 个数的合法子序列方案数,得:
\[f_{i,j} = f_{i-1,j} + f_{pos,j-1}
\]
其中 \(pos\) 为最后一个满足 \(a_i - a_{pos} \geqslant v\) 的位置,转移时只用考虑 \(pos\) 的贡献,因为之前的合法位置的贡献已经统计到 \(pos\) 了。因为满足单调性,所以求 \(pos\) 用双指针即可。
#include<bits/stdc++.h>
#define maxn 1010
#define p 998244353
using namespace std;
template<typename T> inline void read(T &x)
{
x=0;char c=getchar();bool flag=false;
while(!isdigit(c)){if(c=='-')flag=true;c=getchar();}
while(isdigit(c)){x=(x<<1)+(x<<3)+(c^48);c=getchar();}
if(flag)x=-x;
}
int n,k,ans;
int a[maxn],f[maxn][maxn];
int calc(int v)
{
int pos=0;
for(int i=1;i<=n;++i)
{
f[i][0]=1;
while(a[i]-a[pos+1]>=v) pos++;
for(int j=1;j<=k;++j)
f[i][j]=(f[i-1][j]+f[pos][j-1])%p;
}
return f[n][k];
}
int main()
{
read(n),read(k),f[0][0]=1;
for(int i=1;i<=n;++i) read(a[i]);
sort(a+1,a+n+1);
for(int i=1;i<=a[n]/(k-1);++i) ans=(ans+calc(i))%p;
printf("%d",ans);
return 0;
}