补题链接:2023 中国大学生程序设计竞赛(CCPC)新疆赛区(重现赛)
H:数学
我第一题做出来的是H题
和前一天做的一道四平方和有点像,但这个要求是统计所有的,第一想法想到了dp然后过了。
这个问题要求找到一个正整数 n 的每个正整数 i 的最少正整数平方和分解个数,然后计算
。特别的
。
解题思路
-
初始化:
- 创建
数组,大小为
,用于存储从
到
的每个正整数的最少平方和分解个数。初始时,将所有
元素设为 n。
- 设置
,因为 0 的平方和个数为 0;设置
,因为 1 的平方和个数为 1 (即)。
- 创建
数组,大小为
,用于存储所有小于等于 1e5 的平方数。
- 创建
-
预处理平方数:
- 遍历所有可能的整数 j,计算其平方并存入
ans
数组,以便后续使用。
- 遍历所有可能的整数 j,计算其平方并存入
-
动态规划填表:
- 对于每个正整数
,尝试所有小于等于
的平方数
,更新
为中的最小值,确保得到最小的平方和分解个数。
- 对于每个正整数
-
计算最终结果:
- 初始化
为 1,表示乘积的初始值。
- 遍历
数组从 1 到 n,将每个
乘到上并取模 998244353。
- 初始化
方法分析
- 输入处理:读取输入的正整数 n。
- 初始化:设置
数组初始值为 n,
和
分别初始化为 0 和 1。 - 预处理平方数:预计算所有小于等于
的平方数存入
ans
数组,避免后续重复计算,提高效率。 - 动态规划:通过双重循环更新
数组,每次尝试所有可能的平方数并更新最小分解数,确保
存储最小的平方和分解个数。 - 取模运算:在计算结果时对每一步都进行取模操作,防止数据溢出,并满足题目要求。
#include <bits/stdc++.h>
#define simeple freopen("input", "r", stdin),freopen("output", "w", stdout);
#define fast ios::sync_with_stdio(false),cin.tie(0);cout.tie(0);
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
const int MOD = 998244353;
int main() {
fast
// simeple
int n;
cin >> n;
vector<int> dp(1e5 + 5); // dp 数组,用于存储每个数的最少平方和分解个数
vector<int> ans(1e5 + 5); // ans 数组,用于存储所有小于等于 1e5 的平方数
// 初始化 dp 数组的值为 n,表示初始时的最大分解个数
for (int i = 1; i <= n; i++) {
dp[i] = n;
}
dp[1] = 1; // 1 可以用 1 个平方数表示
dp[0] = 0; // 0 可以用 0 个平方数表示
// 预处理所有小于等于 1e5 的平方数
for (int i = 1; i * i <= 1e5; i++) {
ans[i] = i * i;
}
// 动态规划计算每个数的最少平方和分解个数
for (int i = 2; i <= n; i++) {
for (int j = 1; j * j <= 1e5; j++) {
if (i >= ans[j]) {
dp[i] = min(dp[i - ans[j]] + 1, dp[i]); // 更新 dp[i] 的值为最小分解数
} else {
break; // 如果平方数大于 i,则退出循环
}
}
}
long long cnt = 1; // 乘积初始值为 1
// 计算最终结果,将所有 dp[i] 的乘积取模
for (int i = 1; i <= n; i++) {
cnt = cnt * dp[i] % MOD;
}
cout << cnt;
return 0;
}
D:在此同步
第二题做出来的是D题,个人感觉算一个思维题。
题目要求我们找到一个与给定排列 长度相同且同步值(即逆序对数)也相同的排列。我们需要通过适当的交换使得新的排列与原排列的同步值一致。
解题思路
-
输入处理:
- 读取输入的正整数
。
- 读取长度为
的排列
。
- 读取输入的正整数
-
寻找符合条件的位置进行交换:
- 遍历数组中的每个位置
(从
到
),检查
是否符合一个峰值或谷值的条件,即满足
或&&
&&
。 - 若找到满足条件的位置,判断其前后的元素是否能进行交换,从而不改变逆序对数。
- 根据元素的大小关系决定如何交换,确保交换后新排列的逆序对数与原排列一致。
- 遍历数组中的每个位置
-
输出结果:
- 若找到合适的交换位置并完成交换,则输出新的排列。
- 若遍历完所有元素未找到合适的交换位置,则输出
表示无法找到符合条件的排列。
方法分析
- 输入处理:读取输入的正整数
及长度为
的排列
。
- 遍历寻找交换位置:通过检查每个位置
是否为峰值或谷值,找到合适的交换位置。
- 条件交换:根据相邻元素的大小关系决定如何交换,确保交换后新的排列逆序对数与原排列一致。
- 输出结果:若找到合适的位置进行交换,则输出新的排列;否则输出
表示无法找到符合条件的排列。
#include <bits/stdc++.h>
#define simple freopen("input", "r", stdin),freopen("output", "w", stdout);
#define fast ios::sync_with_stdio(false),cin.tie(0);cout.tie(0);
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
int main() {
fast
//simple
int n;
cin >> n;
vector<int> ans(n);
for (int i = 0; i < n; i++) {
cin >> ans[i];
}
// 遍历寻找合适的交换位置
for (int i = 1; i < n - 1; i++) {
// 检查是否为峰值或谷值
if ((ans[i] > ans[i + 1] && ans[i] > ans[i - 1]) || (ans[i] < ans[i + 1] && ans[i] < ans[i - 1])) {
// 判断前后元素是否能进行交换
if (ans[i - 1] < max(ans[i + 1], ans[i]) && ans[i - 1] > min(ans[i], ans[i + 1])) {
swap(ans[i - 1], ans[i + 1]); // 进行交换
swap(ans[i - 1], ans[i]); // 调整元素顺序
} else {
swap(ans[i + 1], ans[i - 1]); // 进行交换
swap(ans[i], ans[i + 1]); // 调整元素顺序
}
for (int j = 0; j < n; j++) {
cout << ans[j] << " ";
}
return 0; // 结束程序
}
}
// 若未找到合适的交换位置,输出 -1
cout << -1;
return 0;
}
剩下的补完题一起写。