2024/11/28 NOIP 模拟赛
数的拆分
Problem
给定 个正整数 ,分别问每个 能否表示为 的形式,其中 为正整数, 为大于等于 的正整数。
对于所有评测用例, 。
Analysis
将原数分解质因数:。当且仅当所有 时满足题目条件。但是直接分解复杂度是错误的,考虑仅用 以内的 来分解原数,当且仅当 剩下的数是平方数或立方数 时条件成立。
证明:剩下的数若满足条件,形式只能为 , 和 三种( 为质数且 )。因为 则更大的幂次不成立。则只有平方数或立方数时成立。
Code
#include <bits/stdc++.h>
using namespace std;
#define int long long
#define PII pair<int, int>
#define _for(i, a, b) for (int i = (a); i <= (b); i++)
#define _pfor(i, a, b) for (int i = (a); i >= (b); i--)
const int N = 2e5 + 5;
int T, n, prime[N], tot;
int check(int x) {
_for(i, 2, x / i) if (x % i == 0) return 0;
return 1;
}
int Sqr(int x) {
int l = 1, r = 1e9;
while (l < r) {
int mid = (l + r + 1) >> 1;
if (mid * mid <= x) l = mid;
else r = mid - 1;
}
return (l * l == x);
}
int Cbr(int x) {
int l = 1, r = 1e6;
while (l < r) {
int mid = (l + r + 1) >> 1;
if (mid * mid * mid <= x) l = mid;
else r = mid - 1;
}
return (l * l * l == x);
}
int read() {
char c = ' ';
int f = 1, x = 0;
while (c < '0' || c > '9') {
if (c == '-') f = -1;
c = getchar();
}
while (c >= '0' && c <= '9') {
x = x * 10 + c - '0';
c = getchar();
}
return x * f;
}
void wr(int x) {
if (x < 0) putchar('-'), x = -x;
if (x > 9) wr(x / 10);
putchar(x % 10 + '0');
}
signed main() {
// freopen("number5.in", "r", stdin);
// freopen("number5.out", "w", stdout);
cin >> T;
_for(i, 2, 4000) {
if (check(i)) prime[++tot] = i;
}
while (T--) {
n = read();
int flg = 0;
_for(i, 1, tot) {
int cnt = 0;
if (n % prime[i] == 0) {
while (n % prime[i] == 0) cnt++, n /= prime[i];
if (cnt == 1) flg = 1;
}
}
if (!flg) {
if (Cbr(n) || Sqr(n)) puts("yes");
else puts("no");
}
else puts("no");
}
}
P8863 「KDOI-03」构造数组
Problem
你现在有一个长度为 的数组 。一开始,所有 均为 。给出一个同样长度为 的目标数组 。求有多少种方案,使得通过若干次以下操作,可以让 数组变成 。
- 选出两个不同的下标 ,并将 和 同时增加 。
两种方案被称之为不同的,当且仅当存在一个 使得一种方案中第 次操作选择的两个下标 与另一种方案中的不同。
, |
Analysis
首先这道题部分分是很足的。测试点 输出 或 。测试点 直接暴力 dfs。测试点 满足 ,可以找到规律。完成上述部分可以拿到 64 分。
正解:计算出操作次数 。把 次操作看成 个二元组,然后把 个 放入这 个二元组中,二元组中不考虑顺序,也不能存在相同的数,问填入所有数的方案。考虑转化后的方案如何对应原来的方案,从填入数值层面考虑,发现考虑每个数值填入哪些二元组和方案一一对应:
例如 :
有了上述思路,可以定义 表示填完前 个数值,存在 个二元组只有一个数,存在 个二元组只有两个数的方案数。可以推算一个数都没填的二元组数量 。转移:
复杂度 。
但是注意到考虑前 数值的 。则可以直接删除 这一维。
复杂度 。因为内层的 限制于 ,则总枚举量为 。
Code
#include <bits/stdc++.h>
using namespace std;
#define PII pair<int, int>
#define _for(i, a, b) for (int i = (a); i <= (b); i++)
#define _pfor(i, a, b) for (int i = (a); i >= (b); i--)
#define int long long
const int N = 5000 + 5, M = 30010, mod = 998244353;
int n, m, a[N], f[2][M], sum[N], fac[M], invfac[M];
int qpow(int a, int k, int p) {
int res = 1;
while (k) {
if (k & 1) res = res * a % p;
a = a * a % p;
k >>= 1;
}
return res;
}
void init() {
fac[0] = invfac[0] = 1;
_for(i, 1, M - 5) {
fac[i] = fac[i - 1] * i % mod;
invfac[i] = invfac[i - 1] * qpow(i, mod - 2, mod) % mod;
}
}
int C(int n, int m) {
if (n < m || n < 0 || m < 0) return 0;
return fac[n] * invfac[n - m] % mod * invfac[m] % mod;
}
signed main() {
cin >> n; init();
_for(i, 1, n) cin >> a[i], m += a[i], sum[i] = sum[i - 1] + a[i];
if (m % 2 == 1) puts("0"), exit(0);
m /= 2;
f[0][0] = 1;
_for(i, 0, n - 1) {
_for(m2, 0, m) {
int m1 = sum[i] - 2 * m2, m0 = m - m1 - m2;
if (m0 < 0 || m1 < 0) continue;
_for(k, 0, a[i + 1]) {
if (m2 + k <= m) f[(i + 1) & 1][m2 + k] = (f[(i + 1) & 1][m2 + k] + f[i & 1][m2] * C(m1, k) % mod * C(m0, a[i + 1] - k) % mod) % mod;
}
}
memset(f[(i + 2) & 1], 0, sizeof f[(i + 2) & 1]);
}
cout << f[n & 1][m] << endl;
}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 无需6万激活码!GitHub神秘组织3小时极速复刻Manus,手把手教你使用OpenManus搭建本
· Manus爆火,是硬核还是营销?
· 终于写完轮子一部分:tcp代理 了,记录一下
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通