洛谷 P3868 [TJOI2009]猜数字
洛谷 P3868 [TJOI2009]猜数字
思路
中国剩余定理 + 快速乘
题目要求找到最小的\(n\in \N\),满足对于\(\forall i\in [1,k]\),有\(b_i | (n-a_i)\)
我们试着来转化一下这个式子
\(b_i|(n-a_i)\),也就是说\((n-a_i)\)在模\(b_i\)意义下同余\(0\),即\(n - a_i\equiv 0(\text{mod}\ b_i)\),进一步转化,就能得到\(n\equiv a_i(\text{mod}\ b_i)\),转化到多个式子就是
\[\begin{equation}
\left\{
\begin{array}{lr}
n \equiv a_1 (\text{mod}\ b_1)\\
n \equiv a_2 (\text{mod}\ b_2)\\……\\
n \equiv a_k (\text{mod}\ b_k)
\end{array}
\right.
\end{equation}\]
这个式子是不是很眼熟?没错,就是中国剩余定理的式子,因为题目中已经保证了\(b_i\)两两互素,所以我们就可以直接套中国剩余定理的板子了
设\(N=\prod_{i=1}^nb_i\),\(Mi=N/b_i\),\(x_i\)是线性同余方程\(M_ix_i≡1(\text{mod}\ b_i)\)的一个解(即\(Mi\)的逆元),最后的解即为\(ans=\sum\limits_{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 << '\n';
return 0;
}
转载不必联系作者,但请声明出处