CF1188C Array Beauty

CF1188C Array Beauty

题目大意

给出一个序列 a [ 1... n ] a[1...n] a[1...n] 还有一个 k k k

问a的所有长度为 k k k的子序列的价值和是多少

定义一个序列 b b b的价值为 m i n ( a b s ( b i − b j ) ) min(abs(b_i - b_j)) min(abs(bibj)) 即最接近的两个数的差

题解

首先因为是子序列,所有可以把a先排个序

然后发现 :

假设最大的数是 a [ n ] a[n] a[n],则对于这个序列价值 <= a [ n ] k − 1 \frac{a[n]}{k - 1} k1a[n]

这个很容易证明吧,就不说了
然后可以很容易想到枚举价值,然后dp出当前价值的有多少种取法,然后乘一下再加起来
但是发现这个dp很难写(反正我不会)
考虑换一种dp方式,dp出大于当前价值的有多少种取法
然后把全部的加起来就行了
为什么呢?
假设当前枚举的价值为v,那么对于1…v 的dp出来的都有1的贡献,就相当于产生了v的贡献
然后加个前缀和优化就好了

看代码好了

code:


#include<bits/stdc++.h>
#define mod 998244353
using namespace std;
int n, k, a[1005], f[1005][1005];
int calc(int x){
	int pre = 0; f[0][0] = 1;
	for(int i = 1; i <= n; i ++){
		while(a[pre] <= a[i] - x) pre ++;//前缀和优化
		f[i][0] = 1;
		for(int j = 1; j <= k; j ++){
			f[i][j] = (f[i - 1][j] + f[pre - 1][j - 1]) % mod;//做前缀和,f[i-1][j]表示不选, 另一个表示选
		}
	}
	return f[n][k];//返回大于价值x的长度为k的子序列个数
}
int main(){
	scanf("%d%d", &n, &k);
	for(int i = 1; i <= n; i ++) scanf("%d", &a[i]);
	sort(a + 1, a + 1 + n); a[0] = - mod;
	int ans = 0;
	for(int i = 1; (k - 1) * i <= a[n]; i ++) ans = (ans + calc(i)) % mod;//枚举价值并加起来
	printf("%d", ans);
	return 0;
}

坑点

貌似没有

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值