CodeForces 1848E Vika and Stone Skipping
感觉比这场的 F 简单。
发现我们要进行 \(x\) 不断乘一些数然后求答案的操作,猜测答案与 \(x\) 的因数有关。如果你对数字比较敏感,不难发现答案就是 \(x\) 的奇因子个数。
官方题解的证明:
设 \(x = f + (f - 1) + \cdots + (f - (c - 1))\),由等差数列求和公式可得 \(x = \frac{(2f - (c - 1))c}{2}\)。
若 \(c\) 为奇数,那么 \(x = (f - \frac{c - 1}{2}) \times c = p \times (2k + 1)\),其中 \(k = \frac{c - 1}{2}, p = f - k\)。因为 \(c - 1 < f\),所以 \(p > k\)。
若 \(c\) 为偶数,那么 \(x = (2f - (c - 1)) \times \frac{c}{2} = (2k + 1) \times p\),其中 \(p = \frac{c}{2}, k = f - p\)。因为 \(c \le f\),所以 \(p \le k\)。
我们发现对于 \(x\) 的每个奇因子 \(2k + 1\),都能唯一对应一个 \(p\)。所以答案就是 \(x\) 的奇因子个数。
于是现在问题变成了,你初始有一个整数 \(x\),\(q\) 次操作,每次操作给出 \(y\),令 \(x \gets x \times y\),每次操作后输出 \(x\) 的奇因子个数。
对 \(x\) 分解质因数,得到 \(x = p_1^{k_1} p_2^{k_2} \cdots p_m^{k_m}\)。考虑 \(x\) 的奇因子是怎么凑成的。对于每个奇质因子 \(p_i\),都可以选 \([0, k_i]\) 个。因此答案是 \(\prod\limits_{2 \nmid p_i} (k_i + 1)\)。
直接的想法是用 map
动态维护答案,每次操作时,若奇质因子 \(p_i\) 的指数改变,就乘一个之前它的 \(k_i + 1\) 的逆元,再乘上现在的 \(k_i + 1\)。但是尽管题目保证模数是质数,仍然有可能存在 \(k_i + 1\) 不存在逆元的情况(\(mod \mid k_i + 1\))。
于是考虑改用线段树维护 \(p_i \in [1, 10^6]\),\(\prod\limits_{2 \nmid p_i} (k_i + 1)\)。每次操作就相当于单点修改,查询就是查询全局积。对于 \(p_i > 10^6\),因为它只可能在初始的 \(x\) 中出现,所以提前预处理好即可。
时间复杂度 \(O(\sqrt{x} + q \log^2 V)\)。
code
// Problem: E. Vika and Stone Skipping
// Contest: Codeforces - Codeforces Round 885 (Div. 2)
// URL: https://codeforces.com/contest/1848/problem/E
// Memory Limit: 256 MB
// Time Limit: 3000 ms
//
// Powered by CP Editor (https://cpeditor.org)
#include <bits/stdc++.h>
#define pb emplace_back
#define fst first
#define scd second
#define mems(a, x) memset((a), (x), sizeof(a))
using namespace std;
typedef long long ll;
typedef unsigned long long ull;
typedef double db;
typedef long double ldb;
typedef pair<ll, ll> pii;
const int maxn = 1000100;
const int N = 1000000;
ll n, m, mod, mpr[maxn], pr[maxn], tot, a[maxn];
bool vis[maxn];
inline ll qpow(ll b, ll p) {
ll res = 1;
while (p) {
if (p & 1) {
res = res * b % mod;
}
b = b * b % mod;
p >>= 1;
}
return res;
}
inline void init() {
for (int i = 2; i <= N; ++i) {
if (!vis[i]) {
mpr[i] = i;
pr[++tot] = i;
}
for (int j = 1; j <= tot && i * pr[j] <= N; ++j) {
vis[i * pr[j]] = 1;
mpr[i * pr[j]] = pr[j];
if (i % pr[j] == 0) {
break;
}
}
}
}
namespace SGT {
ll tree[maxn << 2];
inline void pushup(int x) {
tree[x] = tree[x << 1] * tree[x << 1 | 1] % mod;
}
void build(int rt, int l, int r) {
if (l == r) {
tree[rt] = a[l];
return;
}
int mid = (l + r) >> 1;
build(rt << 1, l, mid);
build(rt << 1 | 1, mid + 1, r);
pushup(rt);
}
void update(int rt, int l, int r, int x, ll y) {
if (l == r) {
tree[rt] = y;
return;
}
int mid = (l + r) >> 1;
(x <= mid) ? update(rt << 1, l, mid, x, y) : update(rt << 1 | 1, mid + 1, r, x, y);
pushup(rt);
}
}
void solve() {
scanf("%lld%lld%lld", &n, &m, &mod);
map<ll, ll> mp;
for (ll i = 2; i * i <= n; ++i) {
if (n % i == 0) {
int cnt = 0;
while (n % i == 0) {
n /= i;
++cnt;
}
mp[i] += cnt;
}
}
ll ans = 1;
if (n > 1) {
++mp[n];
}
for (pii p : mp) {
if (p.fst & 1) {
if (p.fst <= N) {
a[p.fst] = p.scd;
} else {
ans = ans * (p.scd + 1) % mod;
}
}
}
for (int i = 1; i <= N; ++i) {
++a[i];
}
SGT::build(1, 1, N);
while (m--) {
scanf("%lld", &n);
while (n > 1) {
ll t = mpr[n];
++mp[t];
if (t & 1) {
SGT::update(1, 1, N, t, mp[t] + 1);
}
n /= t;
}
printf("%lld\n", ans * SGT::tree[1] % mod);
}
}
int main() {
init();
int T = 1;
// scanf("%d", &T);
while (T--) {
solve();
}
return 0;
}