← 全部文章

CF571E Geometric Progressions 题解

本文目录(3 节)
  1. 题意
  2. 题解
  3. 代码

CF571E Geometric Progressions

题意

  • 给定 nn 以及 nn 个正整数对 ai,bia_i, b_i。
  • 第 ii 对 ai,bia_i, b_i 确定了一个序列 {ai,aibi,aibi2,aibi3,…}\{a_i, a_i b_i, a_i b_i^2, a_i b_i^3, \ldots \}。
  • 询问最小的在 nn 个序列中都有出现的数,或者判断不存在。
  • n≤100n \le 100,ai,bi≤109a_i, b_i \le {10}^9,答案对 109+7{10}^9 + 7 取模。

题解

本题数据范围相对较小,因此保证正确性即可。

首先,我们显然要将所有 ai,bia_i,b_i 分解质因数,根号试除即可。

在先处理掉某个 aia_i 就是答案的情况之后,设 P(x)P(x) 为 xx 的质因数集合,若存在 i,ji,j 有 P(ai)∪P(bi)≠P(aj)∪P(bj)P(a_i) \cup P(b_i) \ne P(a_j) \cup P(b_j),则显然无解。

否则,设 P=P(ai)∪P(bi)P = P(a_i) \cup P(b_i)。

设 cic_i 表示最终的答案在第 ii 个序列中被表示成 aibicia_ib_i^{c_i},另外设 cp(x)c_p(x) 表示 xx 中质数 pp 的次数。

每次考虑合并两个序列 i,ji,j,即对于 p∈Pp \in P,需要找到满足 cp(ai)+ci⋅cp(bi)=cp(aj)+cj⋅cp(bj)c_p(a_i) + c_i \cdot c_p(b_i) = c_p(a_j) + c_j \cdot c_p(b_j) 的 cic_i 的取值。

那么对于每个 p∈Pp \in P,我们都能列出一个这样的方程。

解这个方程组,结果要么判断出无解,要么直接确定 cic_i 的值,要么要解一个不定方程。

前面两种情况都很好处理。对于最后一种情况,可以用扩展欧几里得求出最小非负整数解,若设为 xx,则合并后的新序列中,a′=ai⋅bixa^{\prime} = a_i \cdot b_i^x,b′=∑pplcm⁡(cp(bi),cp(bj))b^{\prime} = \sum_{p} p^{\operatorname{lcm}(c_p(b_i), c_p(b_j))}。

合并到最后,a′a^{\prime} 即为答案。

整个过程中,涉及到的数肯定会很大,因此直接把每个数用其唯一分解式存就行了。

注意,指数会爆 int 但不会爆 long long。

代码

#define Fail print(-1), exit(0)
const int N = 107;
int n;
struct Num {
	vector<pair<int, ll>> p;
	inline void in() {
		int x;
		rd(x);
		for (int i = 2; i * i <= x; i++)
			if (!(x % i)) {
				p.pb(mp(i, 0ll));
				while (!(x % i)) ++p.back().se, x /= i;
			}
		if (x != 1) p.pb(mp(x, 1ll));
	}
	inline void out() {
		modint ans = 1;
		for (auto o : p) ans *= (modint)o.fi ^ o.se;
		print(ans);
	}
	#define s(o) o.p.size()
	#define x(o) x.p[o]
	#define y(o) y.p[o]
	#define z(o) z.p.pb(o)
	inline friend Num operator * (Num x, Num y) {
		Num z;
		ui i = 0, j = 0;
		while (i < s(x) && j < s(y))
			if (x(i).fi == y(j).fi)
				z(mp(x(i).fi, x(i).se + y(j).se)), ++i, ++j;
			else if (x(i).fi < y(j).fi) z(x.p[i++]);
			else z(y.p[j++]);
		while (i < s(x)) z(x.p[i++]);
		while (j < s(y)) z(y.p[j++]);
		return z;
	}
	inline friend bool operator % (Num x, Num y) {
		for (ui i = 0, j = 0; j < s(y); i++, j++) {
			while (i < s(x) && x(i).fi != y(j).fi) ++i;
			if (i == s(x) || x(i).se < y(j).se) return 1;
		}
		return 0;
	}
	inline friend Num operator / (Num x, Num y) {
		Num z;
		for (ui i = 0, j = 0; i < s(x); i++)
			if (j < s(y) && x(i).fi == y(j).fi) {
				z(mp(x(i).fi, x(i).se - y(j++).se));
				if (!z.p.back().se) z.p.pop_back();
			} else z(x(i));
		return z;
	}
	inline friend Num operator & (Num x, Num y) {
		Num z;
		for (ui i = 0, j = 0; i < s(x); i++)
			if (j < s(y) && x(i).fi == y(j).fi)
				z(mp(x(i).fi, x(i).se - y(j++).se));
			else z(x(i));
		return z;
	}
	inline friend bool operator | (Num x, Num y) {
		if (!s(x)) return 0;
		ll k;
		for (ui i = 0, j = 0; i <= s(x); i++, j++) {
			while (j < s(y) && !y(j).se) ++j;
			if (i == s(x)) {
				if (j == s(y)) return 0;
				return 1;
			}
			if (j == s(y)) return 1;
			if (x(i).fi != y(j).fi || x(i).se % y(j).se) return 1;
			if (!i) k = x(i).se / y(j).se;
			else if ((x(i).se / y(j).se) != k) return 1;
		}
		return 0;
	}
	inline friend Num operator ^ (Num x, ll y) {
		for (auto &o : x.p) o.se *= y;
		return x;
	}
	inline friend Num operator + (Num x, Num y) {
		Num z;
		for (ui i = 0; i < s(x); i++)
			z(mp(x(i).fi, x(i).se * y(i).se / __gcd(x(i).se, y(i).se)));
		return z;
	}
} a[N], b[N], c[N], A, B;

inline bool pd(Num x) {
	for (int i = 1; i <= n; i++)
		if ((x % a[i]) || ((x / a[i]) | b[i])) return 0;
	return 1;
}

struct Pro {
	ll k, b, p;
	inline Pro() {}
	inline Pro(ll k, ll b, ll p) : k(k), b(b), p(p) {}
	inline bool operator == (const Pro o) const {
		return k == o.k && b == o.b && p == o.p;
	}
};

inline ll solve(Pro x, Pro y) {
	ll a = x.b * y.p - y.b * x.p, b = x.k * y.p - y.k * x.p;
	if (!b || a % b) Fail;
	return a / b;
}

ll exgcd(ll a, ll b, ll &x, ll &y, ll d = 0ll) {
	return b ? (d = exgcd(b, a % b, y, x), y -= a / b * x, d) : (x = 1, y = 0, a);
}

inline bool merge(int o) {
	vector<Pro> pro;
	for (ui i = 0; i < A.p.size(); i++) {
		ll k1 = B.p[i].se, b1 = A.p[i].se;
		ll k2 = b[o].p[i].se, b2 = a[o].p[i].se;
		if (!k1 && !k2) {
			if (b1 != b2) Fail;
			continue;
		}
		if (!k1) {
			if (b1 < b2 || (b1 - b2) % k2) Fail;
			A = a[o] * (b[o] ^ ((b1 - b2) / k2));
			return 0;
		}
		if (!k2) {
			if (b2 < b1 || (b2 - b1) % k1) Fail;
			A = A * (B ^ ((b2 - b1) / k1));
			return 0;
		}
		ll d = __gcd(k1, k2), g = b2 - b1;
		if (g % d) Fail;
		g /= d, k1 /= d, k2 /= d;
		if (pro.size()) {
			if (pro[0] == Pro(k1, g, k2)) continue;
			A = A * (B ^ solve(pro[0], Pro(k1, g, k2)));
			return 0;
		}
		pro.pb(Pro(k1, g, k2));
	}
	if (pro.size()) {
		ll k = pro[0].k, b = pro[0].b, p = pro[0].p, x, y;
		b = (b % p + p) % p;
		exgcd(k, p, x, y);
		x = (x * b % p + p) % p;
		A = A * (B ^ x);
		B = B + ::b[o];
	}
	return 1;
}

int main() {
	rd(n);
	for (int i = 1; i <= n; i++) a[i].in(), b[i].in(), c[i] = a[i] * b[i];
	for (int i = 1; i <= n; i++) if (pd(a[i])) return a[i].out(), 0;
	for (int i = 1; i <= n; i++) {
		if (c[i].p.size() != c[1].p.size()) Fail;
		for (ui j = 0; j < c[1].p.size(); j++)
			if (c[i].p[j].fi != c[1].p[j].fi) Fail;
		a[i] = c[i] & b[i], b[i] = c[i] & a[i];
	}
	A = a[1], B = b[1];
	for (int i = 2; i <= n; i++)
		if (!merge(i)) {
			if (pd(A)) return A.out(), 0;
			Fail;
		}
	A.out();
	return 0;
}

标签:数论

评论

    填写昵称即可留言。评论经站主审核后公开,请勿提交私人信息。

    正在连接评论服务…