专栏文章
CF2125E Sets of Complementary Sums 题解
CF2125E题解参与者 2已保存评论 5
文章操作
快速查看文章及其快照的属性,并进行相关操作。
- 当前评论
- 5 条
- 当前快照
- 1 份
- 快照标识符
- @mios16l3
- 此快照首次捕获于
- 2025/12/03 00:12 3 个月前
- 此快照最后确认于
- 2025/12/03 00:12 3 个月前
显然与 中不同元素个数相等,不妨设值为 的元素出现了 次,其中 。设 中的元素是 。
考虑所有 次的情况。此时可以发现 。并且只要 为整数且大于 ,就一定能通过其分别减去所有 ,来解出合法的 。
的情况,也可以找到类似的式子:(怎么理解?一共有 个数,求出 个和,每个和对应缺掉其中一个数,所以加起来的和每个数出现了 次)。只要 , 为正整数即可。
以上式子我们可以发现 的存在是特殊的,所以将 与其他 分离化简,得到 ,左边有上界 ,右边有下界 ,那么是否只要满足 就可以了呢?确实如此,在此情况下,我们可以通过令 都为 , 为 ,取合适的正整数 使得 成立。
现在问题转化为了求有多少组 满足 。注意到 都比较接近 ,记 ,则有 。又可以转化为选出 个 中不同的数,若他们的和为 ,则能贡献 种方案( 的方案数),因此只需要求出选出 个 中不同的数,和为 的方案数 。先特判掉 的情况(这是简单的,数组 的 Complementary Sum 为 )。
由此我们可以发现 不能超过 ,否则和一定超了。当然直接做背包还是做不了,因为你要记录当前的和,当前选了几个数,外面再套一层枚举 显然时间复杂度爆了。这里我们可以考虑做类似 Lesbegue 积分的操作,记 表示 中大于等于 的数的个数,则有 ,原理见下图。

容易得到 ,且 ,这样子转化的好处是所有 中 必然都出现了大于等于 次,特别地, 必然出现了大于等于 次,而不再有选数个数的限制。依次考虑 ,每次加入一个新的 ,令 即可,实际上是一个完全背包。具体实现可以参考代码。
CPP#include <bits/stdc++.h>
const int mod = 998244353;
int mul(int x, int y){
return 1ll * x * y % mod;
}
int minus(int x, int y){
x -= y;
if(x < 0) x += mod;
return x;
}
int add(int x, int y){
x += y;
if(x >= mod) x -= mod;
return x;
}
int Qpow(int x, int y){
int r = 1;
while(y){
if(y & 1) r = mul(r, x);
x = mul(x, x); y >>= 1;
}
return r;
}
int n, x, f[200005], g[200005];
void solve(){
scanf("%d%d", &n, &x);
if(n == 1){
printf("%d\n", x);
return ;
}
int ans = 0;
if(1ll * (n + 2) * (n - 1) / 2 > x){
printf("0\n");
return ;
}
for(int i = 0; i <= x; ++i) f[i] = g[i] = 0;
f[0] = 1;
for(int i = 1; i <= n - 1; ++i){
for(int j = 0; j <= x; ++j){
if(j < i) g[j] = 0;
else g[j] = add(g[j - i], f[j - i]);
}
for(int j = x; j >= 0; --j){
f[j] = g[j];
if(i == n - 1 && j >= i) f[j] = minus(f[j], f[j - i]);
}
}
for(int j = 0; j <= x; ++j) ans = add(ans, mul(f[j], x - j + 1));
printf("%d\n", ans);
return ;
}
int main(){
int T = 1;
scanf("%d", &T);
while(T--) solve();
return 0;
}
相关推荐
评论
共 5 条评论,欢迎与作者交流。
正在加载评论...