洛谷 P3868 [TJOI2009]猜数字

洛谷 P3868 [TJOI2009]猜数字

思路

中国剩余定理 + 快速乘

题目要求找到最小的(nin N),满足对于(forall iin [1,k]),有(b_i | (n-a_i))

我们试着来转化一下这个式子

(b_i|(n-a_i)),也就是说((n-a_i))在模(b_i)意义下同余(0),即(n - a_iequiv 0( ext{mod} b_i)),进一步转化,就能得到(nequiv a_i( ext{mod} b_i)),转化到多个式子就是

[egin{equation} left{ egin{array}{lr} n equiv a_1 ( ext{mod} b_1)\ n equiv a_2 ( ext{mod} b_2)\……\ n equiv a_k ( ext{mod} b_k) end{array} ight. end{equation}]

这个式子是不是很眼熟?没错,就是中国剩余定理的式子,因为题目中已经保证了(b_i)两两互素,所以我们就可以直接套中国剩余定理的板子了

(N=prod_{i=1}^nb_i)(Mi=N/b_i)(x_i)是线性同余方程(M_ix_i≡1( ext{mod} b_i))的一个解(即(Mi)的逆元),最后的解即为(ans=sumlimits_{i=1}^ka_iM_ix_i)

那么这样就完了嘛?

此题终结其实不然

这样交上去之后,会发现只有九十分,最后一个点(WA)了,因为这题要用快速乘,于是写上快速乘

那么这样就完了嘛?

此题终结其实也不然

又交上去之后,发现还是九十分,不过这次错的点是第二个了。

这是为什么呢?

看题目条件:(∣a_i∣≤10^9)

什么意思呢?意思就是(a_i)可能是负的!(出题人真是用心良苦

处理的方法就是:快速乘传参时取模

那么这样就完了嘛?

兴冲冲的交上去,终于满分了,没错,这次真完了

代码

/*
Author:loceaner
中国剩余定理板子题 
*/
#include <cmath>
#include <cstdio>
#include <cstring>
#include <iostream>
#include <algorithm>
#define int long long
using namespace std;

const int A = 5e5 + 11;
const int B = 1e6 + 11;
const int mod = 1e9 + 7;
const int inf = 0x3f3f3f3f;

inline int read() {
	char c = getchar(); int x = 0, f = 1;
	for( ; !isdigit(c); c = getchar()) if(c == '-') f = -1;
	for( ; isdigit(c); c = getchar()) x = x * 10 + (c ^ 48);
	return x * f;
}

int n, N = 1, b[A], a[A], ans;

inline int mul(int n, int m, int mod) {
	int res = 0;
	while (m) {
		if (m & 1) res = (res + n) % mod;
		n = (n + n) % mod, m >>= 1;
	}
	return res;
}

inline void exgcd(int a, int b, int &x, int &y) {
	if (!b) x = 1, y = 0;
	else exgcd(b, a % b, y, x), y -= a / b * x;
}

signed main() {
	n = read();
	for (int i = 1; i <= n; i++) a[i] = read();
	for (int i = 1; i <= n; i++) b[i] = read(), N *= b[i];
	for (int i = 1; i <= n; i++) {
		int x, y, Mi = N / b[i];
		exgcd(Mi, b[i], x, y);
		ans = (ans + mul(mul(Mi, x % N + N, N), a[i] % N + N, N) % N + N) % N; 
	}
	cout << ans % N << '
';
	return 0;
}
原文地址:https://www.cnblogs.com/loceaner/p/12765133.html