CodeForces 1848E Vika and Stone Skipping

发布时间 2023-07-17 19:47:56作者: zltzlt

洛谷传送门

CF 传送门

感觉比这场的 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\) 不存在逆元的情况(\(k_i + 1 \mid mod\))。

于是考虑改用线段树维护 \(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;
}