Link.
Description.
一个序列的权值定义为其中任意两个数之间绝对值的最小值。
计算所有长度为 \(k\) 的子序列权值之和 \(\text{mod}\ 998244353\)
Solution.
感觉到了是个 dp
,没想到要去枚举答案和差分
首先,答案范围是 \(\frac{V}k\) 所以我们枚举答案算数量
但是,答案恰好为 \(x\) 的也很难算,所以我们考虑差分,算出答案 \(\ge x\) 的方案数
设计一个 \(dp_{i,j}\) 表示当前第 \(i\) 位,选出了 \(j\) 个的方案数
\(dp[i][j]=dp[k][j-1]\) 之和,然后刚好可以双指针+前缀和维护
需要满足 \(a[i]-a[k]>=lim\)
复杂度是 \(O(\frac Vk\times k\times n)=O(V\times n)\) 可过
Coding.
点击查看弱鸡代码
//是啊,你就是那只鬼了,所以被你碰到以后,就轮到我变成鬼了{{{
#include<bits/stdc++.h>
using namespace std;typedef long long ll;
template<typename T>inline void read(T &x)
{
x=0;char c=getchar(),f=0;
for(;c<48||c>57;c=getchar()) if(!(c^45)) f=1;
for(;c>=48&&c<=57;c=getchar()) x=(x<<1)+(x<<3)+(c^48);
f?x=-x:x;
}/*}}}*/
const int P=998244353;
int n,K,a[1005],mx=0,rs[100005],dp[1005][1005],sm[1005][1005];
inline void solve(int lim)
{
dp[0][0]=1;for(int i=0;i<=n;i++) sm[0][i]=1;
for(int j=1;j<=K;j++)
{
int it=0;for(int i=1;i<=n;i++)
{
while(a[i]-a[it+1]>=lim) it++;
dp[j][i]=sm[j-1][it];
}
//for(int i=1;i<=n;i++) printf("%d%c",dp[j][i],i==n?'\n':' ');
for(int i=1;i<=n;i++) sm[j][i]=(sm[j][i-1]+dp[j][i])%P;
}
for(int i=1;i<=n;i++) rs[lim]=(rs[lim]+dp[K][i])%P;
//printf("%d : %d\n",lim,rs[lim]);
}
int main()
{
read(n),read(K);for(int i=1;i<=n;i++) read(a[i]),mx=max(mx,a[i]);
int L=mx/(K-1);sort(a+1,a+n+1),a[n+1]=1e9;for(int i=1;i<=L;i++) solve(i);
for(int i=1;i<=L;i++) rs[i]=(rs[i]-rs[i+1]+P)%P;
int ans=0;for(int i=1;i<=mx/(K-1);i++) ans=(ans+1ll*i*rs[i])%P;
return printf("%d\n",ans),0;
}