[luogu P3868] [TJOI2009]猜数字

我扔:https://www.luogu.org/problemnew/show/P3868

题意

在这里插入图片描述

就是求这个 x x x
裸的中国剩余定理,什么?你不知道中国剩余定理。好吧,我讲讲。
首先,看上面那一坨式子,要满足

m 1 , m 2 , m 3 , . . . . , m k m_1, m_2, m_3, ...., m_k m1,m2,m3,....,mk两两互质

那么我们设
M = m 1 × m 2 × m 3 × . . . . × m k M = m1 \times m2 \times m3 \times .... \times mk M=m1×m2×m3×....×mk
M i = M / m i M_i = M / m_i Mi=M/mi
则对于 x ≡ a i ( m o d    m i ) x \equiv a_i (\mod m_i) xai(modmi), 我们可以先求出 M i 在 m o d    m i 意 义 下 的 逆 元 M_i 在 \mod m_i 意义下的逆元 Mimodmi(扩欧就行了)
设为 M i − 1 M_i^{-1} Mi1
可得 M i × M i − 1 ≡ 1 ( m o d    m i ) M_i \times M_i^{-1} \equiv 1 (\mod m_i) Mi×Mi11(modmi)
所以 a i × M i × M i − 1 ≡ a i ( m o d    m i ) a_i \times M_i \times M_i^{-1} \equiv a_i (\mod m_i) ai×Mi×Mi1ai(modmi)
又因为 M i = M / m i M_i = M / m_i Mi=M/mi
所以对于所有的 j ≠ i j \neq i j̸=i ( j j j 不等于 i i i
a i × M i × M i − 1 ≡ 0 ( m o d    m j ) a_i \times M_i \times M_i^{-1} \equiv 0(\mod m_j) ai×Mi×Mi10(modmj)
所以只需要把所有的 a i × M i × M i − 1 a_i \times M_i \times M_i^{-1} ai×Mi×Mi1 加起来就是答案了

int crt(){
    int M = 1, x, y, ans = 0;
    for(int i = 1; i <= n; i ++) M = M * b[i]; //求M
    for(int i = 1; i <= n; i ++){
        int m = M / b[i]; //即Mi
        exgcd(b[i], m, x, y); //求逆元,求得为y
        ans =(ans + y * m * a[i]) % M;//累加答案
    }
    if(ans < 0) ans += M;//判断一下
    return ans;
}

那对于这题呢?
Ctrl + c + Ctrl + v
直接套板子?

W A   o n   # 10 {\color{red} WA \\\\\ on\\\ \# 10} WA on #10

W T F ? ? {\color{white} WTF??} WTF??

????

注意题目中的一句话

所有数据中,第一组数字的绝对值不超过 1 0 9 10^9 109(可能为负数),第二组数字均为不超过 6000 6000 6000的正整数,且第二组里所有数的乘积不超过 1 0 18 10^{18} 1018

1 0 18 10^{18} 1018 ??!! 乘起来不就 1 0 36 10^{36} 1036 爆long long辣

那怎么办
于是就翻题解学习了一个叫做快速乘的东西。。
快速乘就是用来处理在爆long long 的边缘来回试探 的乘法下要取模的一种操作
其实应该叫做龟速乘
核心思想就是把一个数按二进制拆分,然后一位一位对应乘。
看代码感性理解一下吧:

int ksc(int a, int b, int mod){
	int ans = 0;
	for(;b; b >>= 1, a = (a + a) % mod) if(b&1) ans = (ans + a) % mod;
	//解释一下, 是把 b 按二进制位拆分, a = a + a 就是每次将 a 乘 2(b每次除2, a肯定要对应乘2嘛)
	//如果b当前位为1,就对答案有贡献
	return ans;
}

然后这题就解决了

吗 ? {\color{red} 吗?}

T L E   o n   # 2 {\color{blue} TLE \\\\\ on\\\ \# 2} TLE on #2

W T F ? ? ? {\color{white} WTF???} WTF???

哦,忘记说了,由于出题人太毒瘤 输入的 a i a_i ai可能为负数,所以在做快速乘之前要把它转为正数

没了?
没了。
等等忘记贴代码了:

#include<bits/stdc++.h>
#define int long long
using namespace std;
int exgcd(int a, int b, int &x, int &y){//扩欧 
	if(!b) {x = 1, y = 0; return a;}
	int d = exgcd(b, a % b, x, y);
	int t = x;
	x = y;
	y = t - a / b * y;
	return d;
}
int ksc(int a, int b, int mod){//快速乘 
	int ans = 0;
	for(;b; b >>= 1, a = (a + a) % mod) if(b&1) ans = (ans + a) % mod;
	return ans;
}
int a[15], b[15], n;
int crt(){
	int M = 1, x, y, ans = 0;
	for(int i = 1; i <= n; i ++) M = M * b[i];//算 M 
	for(int i = 1; i <= n; i ++){
		int m = M / b[i];
		exgcd(b[i], m, x, y);//求逆元 
		y = (y % b[i] + b[i]) % b[i];
		ans =(ans + ksc(y, ksc(m, (a[i] + M) % M, M), M) + M) % M;//快速乘,记得a[i]要转为正数 
	}
	if(ans < 0) ans += M;
	return ans;
}
 main(){
	scanf("%lld", &n);
	for(int i = 1; i <= n; i ++) scanf("%lld", &a[i]);
	for(int i = 1; i <= n; i ++) scanf("%lld", &b[i]);
	printf("%lld", crt());
	return 0;
}

貌似龟速 快速乘是可以做到接近O(1)的

#include<bits/stdc++.h>
#define int long long
using namespace std;
int exgcd(int a, int b, int &x, int &y){
    if(!b) {x = 1, y = 0; return a;}
    int d = exgcd(b, a % b, x, y);
    int t = x;
    x = y;
    y = t - a / b * y;
    return d;
}
int ksc(int a, int b, int mod){//开挂般的接近O(1)的快速乘
    a = (a % mod + mod) % mod, b = (b % mod + mod) % mod;
    return ((a * b - (int)((long double)a / mod * b + 1e-6) * mod) % mod + mod) % mod;
}
int a[15], b[15], n;
int crt(){
    int M = 1, x, y, ans = 0;
    for(int i = 1; i <= n; i ++) M = M * b[i];
    for(int i = 1; i <= n; i ++){
        int m = M / b[i];
        exgcd(b[i], m, x, y);
        y = (y % b[i] + b[i]) % b[i];
        ans =(ans + ksc(y, ksc(m, (a[i] + M) % M, M), M) + M) % M;
    }
    if(ans < 0) ans += M;
    return ans;
}
 main(){
    scanf("%lld", &n);
    for(int i = 1; i <= n; i ++) scanf("%lld", &a[i]);
    for(int i = 1; i <= n; i ++) scanf("%lld", &b[i]);
    printf("%lld", crt());
    return 0;
}

还 不 够 啊 ! ! {\color{white} 还不够啊!!} !!

我是不会告诉你们空白的地方还有字的

posted @ 2019-01-03 09:51  lahlah  阅读(32)  评论(0编辑  收藏  举报