T1:三倍数
本题难度较大,“三倍数”的位数一定是 的倍数。
若 ,则答案为?
答案为 。
若 的位数 满足 , ,则
都是不超过 的三倍数。答案为 。
考虑 为 的倍数的情形
若 分别为 ,,则答案为?
答案分别为 和 。
将 写成三等分的形式 ,若 ,或 ,,则答案即为 。
否则,答案应为 。
不难看出,本题需要考察高精度减法。
由于减数仅仅为 ,所以无需实现真正意义上的高精度减法。
只需实现好退位、错位即可。
代码实现
#include <bits/stdc++.h> #define rep(i, n) for (int i = 0; i < (n); ++i) using namespace std; int a[400], cnt; int main() { string m; cin >> m; int n = m.size(); if (n%3) { cout << string(n/3, '9') << '\n'; return 0; } n /= 3; if (m.substr(0, n) < m.substr(n, n) or (m.substr(0, n) == m.substr(n, n) and m.substr(n, n) <= m.substr(2*n))) { cout << m.substr(0, n) << '\n'; return 0; } for (int i = n-1; i >= 0; --i) { a[++cnt] = m[i]-'0'; } --a[1]; for (int i = 1; i <= cnt and a[i] < 0; ++i) { a[i] += 10; a[i+1]--; } while (cnt > 1 and !a[cnt]) --cnt; for (int i = cnt; i >= 1; --i) { cout << a[i]; } return 0; }
T2:固定和的倍数序列
设 ,某一倍数序列长度为 ,则元素总和可以写作:
可得 ,这一式子是求出状态转移方程的关键。
其表明:固定 ,则一个元素总和为 的倍数序列,与一个元素总和为 的倍数序列一一对应。
状态:
- 要区分出两种倍数序列的元素总和。
- :和为 的倍数序列的数量。
转移:
- 任取 的约数 ,则 可以作为倍数序列的第一项 。
- 所有元素总和为 的倍数序列,按照 分类,该分类方法不重复、不遗漏。
方程:
细节:
- 初始条件:
- 状态转移方程中的 是 的约数,而约数是成对出现的,因此没有必要枚举到底。
- 要注意一对两个约数都是某一完全平方数的平方根的情形,不要重复计数。
时间复杂度:
代码实现
#include <bits/stdc++.h> #define rep(i, n) for (int i = 0; i < (n); ++i) using namespace std; using ll = long long; //const int mod = 998244353; const int mod = 1000000007; struct mint { ll x; mint(ll x=0):x((x%mod+mod)%mod) {} mint operator-() const { return mint(-x); } mint& operator+=(const mint a) { if ((x += a.x) >= mod) x -= mod; return *this; } mint& operator-=(const mint a) { if ((x += mod-a.x) >= mod) x -= mod; return *this; } mint& operator*=(const mint a) { (x *= a.x) %= mod; return *this; } mint operator+(const mint a) const { return mint(*this) += a; } mint operator-(const mint a) const { return mint(*this) -= a; } mint operator*(const mint a) const { return mint(*this) *= a; } mint pow(ll t) const { if (!t) return 1; mint a = pow(t>>1); a *= a; if (t&1) a *= *this; return a; } // for prime mod mint inv() const { return pow(mod-2); } mint& operator/=(const mint a) { return *this *= a.inv(); } mint operator/(const mint a) const { return mint(*this) /= a; } }; istream& operator>>(istream& is, mint& a) { return is >> a.x; } ostream& operator<<(ostream& os, const mint& a) { return os << a.x; } mint dp[100005]; int main() { int m; cin >> m; dp[0] = 1; for (int i = 1; i <= m; ++i) { for (int j = 1; j*j <= i; ++j) { if (i%j) continue; dp[i] += dp[i/j-1]; if (j != i/j) dp[i] += dp[j-1]; } } cout << dp[m] << '\n'; return 0; }
递推/填表法 递拉/刷表法
- 不是对于等号左边的项,将右边所有的项加起来
- 而是对于等号右边的项,考虑它要加到左边的哪些项上。
观察方程 ,以 为主元,可以改写成:
for (int i = 1; i <= m; ++i) { for (int j = 1; j <= m/i; ++j) { dp[i*j] += dp[i-1]; } }
统计双重循环操作次数,有:
在数学上, 称为调和级数。
由阶的估计的知识, 。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 使用C#创建一个MCP客户端
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列1:轻松3步本地部署deepseek,普通电脑可用
· 按钮权限的设计及实现