[AHOI2022] 山河重整 解题报告
T3,一个不错的数学题,给了不少的暴力分。
Statement
求有多少值域为
Subtask
我们从小到大选择每一个数,不难发现凑出来的数字一定是
于是考虑 dp,记
如何转移?考虑当前加
于是转移写成
时间复杂度
Subtask
上述 dp 已经不好优化,考虑重新设计状态。
不妨设计容斥,对于一组不合法方案,找到最小的
那么就可以转化为,对于一个位置
于是状态变为设
更具体地,我们枚举第一个不能被表示出来的数,在计算
均可以被表示 不能被表示 凑出来的数和为
接下来就需要计算
有一个结论:
可以将互异拆分数看作一个阶梯。
如图所示,此时
朴素的题目一般给定了拆分部分
在本题会退化成
但此时这个做法还是与暴力同分。
考虑如何优化构造,我们将整个图竖过来。
那么现在构造转换成了对于
在上张图中,不难发现有大于等于
考虑这样一个数列是递减的,可以从
对于一个
如果我们要让
复杂度为
Code
void work(int n) {
if (n <= 1) return;
work(n / 2);
int lim = sqrt(n << 1);
for (int i = lim; i >= 1; --i) {
for (int j = n; j >= i; --j)
g[j] = g[j - i];
for (int j = 0, k = 2 * i; k <= n; ++j, k += i + 1)
add(g[k], f[j]);
for (int j = i; j <= n; ++j) add(g[j], g[j - i]);
}
for (int i = n / 2 + 1; i <= n; ++i) add(f[i], -g[i] + p);
for (int i = 0; i <= n; ++i) g[i] = 0;
}
int main() {
cin >> n >> p;
pw[0] = 1;
for (int i = 1; i <= n; ++i) pw[i] = (pw[i - 1] << 1) % p;
int lim = sqrt(n << 1);
for (int i = lim; i >= 1; --i) {
for (int j = n; j >= i; --j) f[j] = f[j - i];
add(f[i], 1);
for (int j = i; j <= n; ++j) add(f[j], f[j - i]);
}
f[0] = 1;
work(n);
int ans = 0;
for (int i = 0; i < n; ++i) add(ans, 1ll * f[i] * pw[n - i - 1] % p);
cout << (pw[n] - ans + p) % p << endl;
return 0;
}
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 在鹅厂做java开发是什么体验
· 百万级群聊的设计实践
· WPF到Web的无缝过渡:英雄联盟客户端的OpenSilver迁移实战
· 永远不要相信用户的输入:从 SQL 注入攻防看输入验证的重要性
· 全网最简单!3分钟用满血DeepSeek R1开发一款AI智能客服,零代码轻松接入微信、公众号、小程