『模拟赛题解』9.24 NOIP 模拟赛
9.24 NOIP 模拟赛
T1. 质因数
Description
给你 \(n\) 个数,求出每个数分解质因数后不同的质因数数量(以下简称 \(f(x)\))。
Solution
考场思路
首先先把 \(1 \sim 10^6\) 以内的质数全部筛出来,则这些质数的 \(f = 1\)。
经过一系列的推导,我得到了一个奇怪的结论:
其中 \(k_i\) 为 \(i\) 最小的质因数。
最终得分只有 \(40\) pts。但好像这个式子已经很接近正解了。
正解
正解如下:
\(k_i\) 含义同上,特别的,\(k_1 = 0\)。
那 \(k_i\) 怎么处理呢?其实就是线性筛的一个小变种。
Complexity
时间复杂度:朴素筛法:\(O(n + \max a_i \times log_2 \max a_i)\),线性筛法:\(O(n + \max a_i)\)(\(a_i\) 是输入的数)
空间复杂度:\(O(n)\)
Code
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6;
int n;
int f[maxn + 5], x[maxn + 5], k[maxn + 5], p[maxn + 5];
int read()
{
char ch;
int ans = 0;
ch = getchar();
while (ch < '0' || ch > '9')
ch = getchar();
while (ch >= '0' && ch <= '9')
{
ans = ans * 10 + ch - '0';
ch = getchar();
}
return ans;
}
int main()
{
freopen("easy.in", "r", stdin);
freopen("easy.out", "w", stdout);
n = read();
for (int i = 1; i <= n; i ++)
x[i] = read();
memset(p, 1, sizeof(p));
p[1] = true;
k[1] = 0;
for (int i = 2; i <= maxn; i ++)
{
if (p[i])
{
for (int j = 1; i * j <= maxn; j ++)
{
if (p[i * j])
k[i * j] = i;
p[i * j] = 0;
}
}
f[i] = f[i / k[i]];
if (k[i / k[i]] != k[i])
f[i] ++;
}
for (int i = 1; i <= n; i ++)
cout << f[x[i]] << '\n';
return 0;
}
T2. 编码
Description
每个数字字符串都可以被表示为 \(b_1\) 个 \(c_1\),\(b_2\) 个 \(c_2\),\(\dots\),\(b_n\) 个 \(c_n\)。
将 \(b_1, c_1, b_2, c_2, \dots, b_n, c_n\) 拼接而成的最短字符串成为原字符串的 p 型编码。
e.g. 100200300
可以被表示为 \(1\) 个 \(1\),\(2\) 个 \(0\),\(1\) 个 \(2\),\(2\) 个 \(0\),\(1\) 个 \(3\),\(2\) 个 \(0\)。其 p 型编码为 112012201320
。
易知,每个数字字符串有固定的 p 型编码,但每个 p 型编码没有固定的数字字符串。
给出一个 p 型编码,请求出这个编码对应的字符串有几个。
Solution
考场思路
一顿乱搞,\(30\) pts。
正解
设 \(f_i\) 为 \(1 \sim i\) 这段编码对应的字符串的方案数。
则:
如果直接暴力转移的话,只能得到 \(60\) pts。
注意到,\(f_i\) 其实就是 \(\sum f_j - \sum f_j (a_i \neq a_j)\)。所以只需要在计算的同时计算 \(\sum f\) 和 \(a_i = a_j\) 的情况的和记录下来就好了。
Complexity
时间复杂度:\(O(|a|)\)。
Code
#include <bits/stdc++.h>
using namespace std;
const int maxn = 1e6 + 5;
const int mod = 998244353;
char s[maxn];
int a[maxn], sum[maxn], f[maxn];
int main()
{
freopen("hard.in", "r", stdin);
freopen("hard.out", "w", stdout);
int x, s2 = 0;
scanf("%s", s);
for (x = 1; s[x - 1] != '\0'; x ++)
a[x] = s[x - 1];
int n = x - 1;
f[0] = 1;
for (int i = 1; i <= n; i ++)
{
if (a[i + 1] != '0')
f[i] = (s2 - sum[a[i]] + mod) % mod;
s2 = (s2 + f[i - 1]) % mod;
sum[a[i - 1]] = (sum[a[i - 1]] + f[i - 1]) % mod;
}
cout << f[n];
return 0;
}
上为迷惑 \(90\) pts 的 std。
我也不知道为啥 qwq,先这样吧。
T3. 八卦阵
Description
给定一张 \(n\) 个点, \(m\) 条边的有向图。每条边 \(i\) 有一个困难度 \(a_i\)。
每个点 \(u\) 有八个状态:\(b_{u_1}, b_{u_2}, \dots, b_{u_8}\),这八个状态为均 \([1,8]\)之间的整数(不一定是排列),在第 \(i\) 秒中,\(u\) 的状态为 \(b_{u_{(i-1) \text{mod} 8 + 1}}\)。
对于任意两个状态 \(i,j\) 都存在一个复杂度 \(c_{ij}\)。
在第一秒开始时位于一号点,每次他可以用一秒从当前点 \(u\) 走到另一个有边相连的点 \(v\)。也就是说,在第一秒内走过第一条边,在第二秒内走过第二条边…… 在第 \(i\) 秒通过第 \(j\) 条边从点 \(u\) 走到点 \(v\),需要付出 \(a_j + c_{b_{u,(i - 1) \text{mod} 8 + 1},b_{v, (i - 1) \text{mod} 8 + 1}}\) 的代价。 问从点 \(1\) 走到点 \(n\) 最小的代价。
Solution
考场思路
没写。
正解
考虑分层图的思路,建出八个图,每个图的边权代表在 \(i \ \text{mod} \ 8\) 为特定值时的边权。
每条边与下一层图相连,跑最短路就行了。
Complexity
时间复杂度:\(O(m \log n)\)。
Code
略。