LG2350 [HAOI2012] 外星人
Problem
Analysis
正解思路
题目给出了一个欧拉函数的嵌套形式,且最后结果为 \(1\),启发我们想到欧拉函数嵌套的过程实际是素因子种类数不断减少的过程。
然后通过手动模拟,可以直接验证每个奇素因子最后都会变成 \(2\),再利用 \(\varphi(2)\) 变成 \(1\)。更关键的想法是,想到若 \(N\) 为偶数,则每次取 \(\varphi\) 都会有一个 \(2\) 变成 \(1\),且 \(2\) 一定是最后才消除的,因为有其他奇素因子减一后变成 \(2\) 的倍数进行补充。而若 \(N\) 为奇数,则一开始需要花费一步产生 \(2\),接下来的步骤与偶数情况无异。
于是,可以得到一个结论:若 \(N\) 为偶数,则每次取 \(\varphi\) 都会消耗一个 \(2\),\(x\) 就是所有素因子不断分解后产生的总共 \(2\) 的个数;\(N\) 为奇数则多花费一次操作。
现在的目标就是计算每个素因子不断先分解再取 \(\varphi\) 能产生多少个 \(2\)。这一步较容易想到用 dp 求解,设 \(dp_i\) 为 \(i\) 这样操作能分解出 \(2\) 的个数,转移就是:若 \(i\) 为素数,则 \(dp_i\gets dp_{i-1}\);若 \(i\) 非素数,则 \(dp_i\gets dp_p+dp_{i/p}\)(\(p\) 为 \(i\) 的最小素因子)。可以利用欧拉筛完成。
错因总结
- 我没有想到只有 \(1,2\) 的 \(\varphi\) 等于 \(1\) 这一个事实,去考虑把问题转化为嵌套 \(x-1\) 次后 \(\varphi\) 值为质数或 \(1\),这一直接将问题复杂化了。
- 没有手动模拟数据,其实这是最快最高效的检查自己是否看错题、发现题目结论和启发思路最好方法。
AC Code
点击查看代码
#include <bits/stdc++.h>
#define ll long long
#define ull unsigned long long
#define i128 __int128
#define fi first
#define se second
#define PII pair<int, int>
#define PLL pair<ll, ll>
#define mk make_pair
#define INF 0x3f3f3f3f
#define INFx 0x3f3f3f3f3f3f3f3f
using namespace std;
const int N = 2010, M = 1e5 + 10;
int m;
int p[N], q[N];
int prime[M], tot;
bool st[M];
int dp[M];
void euler_sieve(int n) {
dp[1] = 1;
for (int i = 2; i <= n; i ++) {
if (!st[i]) {
prime[++ tot] = i;
dp[i] = dp[i - 1];
}
for (int j = 1; j <= tot; j ++) {
if (1ll * i * prime[j] > n) break;
st[i * prime[j]] = true;
dp[i * prime[j]] = dp[i] + dp[prime[j]];
if (i % prime[j] == 0) break;
}
}
}
void solve() {
cin >> m;
int ans = 1;
for (int i = 1; i <= m; i ++) {
cin >> p[i] >> q[i];
ans += dp[p[i]] * q[i];
if (p[i] == 2) ans --;
}
cout << ans << '\n';
}
int main() {
ios::sync_with_stdio(false), cin.tie(0), cout.tie(0);
int T;
cin >> T;
euler_sieve(1e5);
while (T --) solve();
return 0;
}

浙公网安备 33010602011771号