P3868 [TJOI2009]猜数字

题目描述

现有两组数字,每组 kk 个。

第一组中的数字分别用 a 1 , a 2 , ⋯   , a k a_1,a_2,\cdots ,a_k a1,a2,,ak表示,第二组中的数字分别用 b 1 , b 2 , ⋯   , b k b_1,b_2,\cdots ,b_k b1,b2,,bk表示。
其中第二组中的数字是两两互素的。求最小的 n ∈ N n\in \mathbb{N} nN,满足对于 ∀ i ∈ [ 1 , k ] \forall i\in [1,k] i[1,k],有 b i ∣ ( n − a i ) b_i | (n-a_i) bi(nai)

输入格式

第一行一个整数 kk。

第二行 kk 个整数,表示: a 1 , a 2 , ⋯   , a k a_1,a_2,\cdots ,a_k a1,a2,,ak
第三行 kk 个整数,表示: b 1 , b 2 , ⋯   , b k b_1,b_2,\cdots ,b_k b1,b2,,bk

输出格式

输出一行一个整数,为所求的答案 n。

输入输出样例

输入

3
1 2 3
2 3 5

输出

23

说明/提示

对于 100 % 100\% 100% 的数据:

1 ≤ k ≤ 10 , ∣ a i ∣ ≤ 1 0 9 , 1 ≤ b i ≤ 6 × 1 0 3 , ∏ i = 1 k b i ≤ 1 0 18 1\le k \le 10,|a_i|\le 10^9,1\le b_i\le 6\times 10^3 ,\prod_{i=1}^k b_i\le 10^{18} 1k10ai1091bi6×103i=1kbi1018

每个测试点时限 1 秒。

注意:对于 C/C++ 语言,对 64 位整型数应声明为 long long。

若使用 scanf,printf 函数(以及 fscanf,fprintf 等),应采用 %lld 标识符。

题解

问题转化为:

⎧ ⎪    a n s ≡ a 1 ( m o d    b 1 ) ⎪    a n s ≡ a 2 ( m o d    b 2 ) ⎨           ⋅ ⋅ ⋅ ⋅ ⋅ ⋅ ⎪    a n s ≡ a n ( m o d    b n ) ⎩ ⎧ \\ ⎪\ \ ans\equiv a_1(mod\ \ b_1)\\ ⎪\ \ ans\equiv a_2(mod\ \ b_2)\\ ⎨ \ \ \ \ \ \ \ \ \ ······\\ ⎪\ \ ans\equiv a_n(mod\ \ b_n)\\ ⎩   ansa1(mod  b1)  ansa2(mod  b2)           ansan(mod  bn)
所以用很 模板的CRT 再加上龟速乘解决数值大的问题即可。
CRT的做法:👆链接里的另一篇真正的模板CRT,此篇就省略了。


这里讲下龟速乘(本人第一次用
由于数据的值太大,直接相乘易爆,于是需要用一个类似快速幂的算法———龟速乘
其实它的思想跟快速幂很像,所以有人叫它快速乘,但其实它的速度并不快。
假如要算 15 ∗ 11   % m o d 15*11\ \%mod 1511 %mod
11 11 11 的二进制为 1011 1011 1011,也就是 11 = 8 + 0 + 2 + 1 11=8+0+2+1 11=8+0+2+1
15 ∗ 11 = 15 ∗ 8 + 15 ∗ 0 + 15 ∗ 2 + 15 ∗ 1 15*11=15*8+15*0+15*2+15*1 1511=158+150+152+151
15 ∗ 2 = 15 + 15 , 15 ∗ 4 = 30 + 30 , 15 ∗ 8 = 60 + 60 15*2=15+15,15*4=30+30,15*8=60+60 152=15+15154=30+30158=60+60

也就是说
15即第一个乘数记为 a a a,11即第二个乘数的二进制记为 b b b
我们从二进制的低位往高位逐位进行两个操作:若 b i = 1 b_i=1 bi=1 a n s + = a ans+=a ans+=a a + = a a+=a a+=a
所以我们就这样将一个乘法活生生变成了加法,且每次加法都可以 % m o d \%mod %mod 就不用担心爆掉了。
代码如下:

ll fmul(ll a,ll b,ll c)
{
	ll sum=0;
	while(b)
	{
		if(b&1) sum=(sum+a)%c; //若此位为 1,则加上
		a=(a+a)%c;
		b>>=1; //右移一位
	}
	return sum;
}

但本题还有个坑人的地方:
再仔细看看数据规模: ∣ a i ∣ ≤ 1 0 9 |a_i|\le 10^9 ai109
也就是说,a可以是负数,所以要加些特别的处理(👇注释中有说),且快读要判负!


代码

#include<iostream>
#include<cstdio>
#define ll long long
using namespace std;
ll n,m=1,ans,t,x,y,a[20],b[20];
ll read()
{
	int x=0,t=1;
	char c=getchar();
	while(c>'9'||c<'0')
	{
		if(c=='-') t=-1;
		c=getchar();
	}
	while(c>='0'&&c<='9')
	{
		x=(x<<1)+(x<<3)+c-48;
		c=getchar();
	}
	return x*t;
}
ll fmul(ll a,ll b,ll c)//龟速乘
{
	ll sum=0;
	while(b)
	{
		if(b&1) sum=(sum+a)%c;
		a=(a+a)%c;
		b>>=1;
	}
	return sum;
}
void exgcd(ll a,ll b)//求逆元
{
	if(b==0)
	{
		x=1;
		y=0;
		return;
	}
	exgcd(b,a%b);
	int t=x;
	x=y;
	y=t-a/b*y;
}
int main()
{
	n=read();
	for(int i=1; i<=n; i++)
	{
		b[i]=read();//a,b反着输入,CRT处理习惯问题
	}
	for(int i=1; i<=n; i++)
	{
		a[i]=read();
		m*=a[i];
	}
	for(int i=1;i<=n;i++) 
	{
		b[i]=(b[i]%a[i]+a[i])%a[i];//特殊处理,以防负数
	}
	for(int i=1; i<=n; i++)
	{
		t=m/a[i];
		x=y=0;
		exgcd(t,a[i]);
		x=(x%a[i]+a[i])%a[i];//特殊处理,以防负数
		ans=(ans+fmul(fmul(x,t,m),b[i],m))%m;//记得用龟速乘
	}
	cout<<ans;
}
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值