[NOI Online #1 入门组] 跑步 题解
[NOI Online #1 入门组] 跑步 题解
一个经典问题:计数将正整数拆分为若干个正整数的方案数,这里拆成的正整数是无序的,对取模。
容易得到解法
设表示用个数得到的方案数。转移即考虑增加一个数为或全部数加。
或者设表示用的数得到的方案数。转移即考虑选不选。
然而似乎无法继续优化。
下面就是本题的关键了。画出图,如图
这里将一行看成一个数。
考虑上面的做法在图上的实质。
第一种做法是每次在下方加一个点或在左方加一列。复杂度为。
第二种做法是每次加一行或者将当前可加行长度加一。复杂度为。
我们想要将行数和列数同时控制在一个合适的范围内,这样把两个做法拼起来或许就能优化。
于是考虑强制第一种做法中每加一个点就加个,第二种做法中至多将当前可加行长度累加至
这样第一种做法中行数至多为复杂度就是
取,可得最优复杂度为
点我看代码 |ू・ω・` )
#include <cstdio> #include <iostream> #define LL long long using namespace std; template <typename T> inline void read(T &x) { x = 0; int f = 0; char ch = getchar(); for(; !isdigit(ch); ch = getchar()) if(ch == '-') f = 1; for(; isdigit(ch); ch = getchar()) x = (x << 3) + (x << 1) + (ch ^ 48); if(f) x = ~x + 1; } const int B = 317; const int N = 1e5 + 10; int n, P, m, ans; int f[N][B + 10], g[N][B + 10]; LL sg[N]; inline void update(int &x, int y) {if((x += y) >= P) x -= P;} int main() { read(n), read(P); for(int i = 0; i <= B; ++i) f[0][i] = 1; for(int i = 1; i <= n; ++i) { for(int j = 1; j < B; ++j) { f[i][j] = f[i][j - 1]; if(i >= j) update(f[i][j], f[i - j][j]); } } m = n / B + 1; sg[0] = g[0][0] = 1; for(int i = 1; i <= n; ++i) { for(int j = 1; j <= m; ++j) { if(i >= B) update(g[i][j], g[i - B][j - 1]); if(i >= j) update(g[i][j], g[i - j][j]); sg[i] += g[i][j]; } sg[i] %= P; } for(int i = 0; i <= n; ++i) update(ans, f[i][B - 1] * sg[n - i] % P); printf("%d\n",ans); }
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现