ztz11
2018-09-15 11:14:26
链接1(PDF下载):https://files.cnblogs.com/files/ztz11/PDF%E7%89%88.rar?t=1732165734&download=true
(请将链接复制到地址栏,直接点击链接会导致下载错误)
链接2(图片版博客)
a%b=d,c%b=e,
则(a+c)%b=(d+e)%b(正确性在此不加证明)
a%b=1,则(d
mian包是一个贪吃的孩子,这天,他买了一堆绿鸟吃。当然他的妈妈并不想让他吃太多食物(因为那样会发胖),为了避免老妈的唠叨,他决定不告诉他的妈妈绿鸟数量,而是将绿鸟的数量x用以下式子来描述
注:
(
由于他的妈妈数学不好,于是就来向你求助了,请你求出mian包最少买了多少烤绿鸟
第一行包含一个整数n (n <= 10) – 告诉妈妈的式子数,接下来n行,每行两个整数ai, bi( bi <= ai <= 1000)
首先,我们把这道题简化成下面的图
样例为:
3
3 1
5 1
7 2
很显眼吧?怎么做呢?
这就很简单了
我们从第二个开始枚举
初始值为a1+b1;
每次自加当前的已经满z足的
如果当前值已经满足
则当前已经成立
ps:上式为什么成立呢?
以上面的例子为例,看下面我的图
当已经满足前两个等式时,我们增加
那么我们会增加15只绿鸟
看图:
很显然,15%3=0,15%5=0
所以增加15的话一定满足当前的等式
自然,暴力就解决了
#include<iostream>
#include<cstdio>
#include<algorithm>
using namespace std;
long long a,b,c,d,e,f,g,kkk,n;
struct zzzz{
int x,y,z;
}ltt[15];
bool cmp(zzzz s,zzzz t)
{
return s.x>t.x;
}
int main()
{
cin>>n;
kkk=1;
for(a=1;a<=n;a++)
{
cin>>ltt[a].x>>ltt[a].y;
}
sort(ltt+1,ltt+n+1,cmp);
kkk=ltt[1].x;
g=ltt[1].y;
f+=2;
while(f<=n)//枚举每个等式
{
while(1)
{
if(g%ltt[f].x==ltt[f].y)
{
kkk=kkk*ltt[f].x;
f++;
break;
}
g=g+kkk;//自加,直到满足当前等式
}
}
cout<<g;
}
看下面这个式子:
好吧,你自加去吧,保证TLE
这时,我们就需要一个高效的算法
每一个crt方程组的解在模
为什么呢?因为每个解之间的差是所有a的乘积,加数不管对序列中的哪个数取模都是0呗
先说点别的东西
由上面的暴力我们可以看出,该样例crt解法的本质是从5和7的公倍数中找一个除以3余1的数
我们设{
然后按照暴力的方法,我们可以得到这样一个式子:
并满足(
首先,因为
而
所以,我们知道上式中除了第
所以,原式 ≡
然后,我们又根据一开始那个式子的约束条件,得出
所以,整个式子满足对
我们将这个推广开来,即可证得原式是crt方程组的一个解
然后根据前置知识三,即求得原始的最小解
我们设
那么,原式就变成了:
且
这是什么?
很多人表示一脸懵逼,不知道该怎么求???
好,让我们仔细看一下
我们由前置定理2可得:
如果
那么当
对于原方程的两个式子,我们可以化成这样(请跟着我的公式):
则
这就是一眼看穿的问题了
所以,这里就能用上面exgcd了
顺次两两合并
每次得出一个当前的解
合并很简单,
我们首先利用exgcd求出一个解
然后利用这个解,新构建一个新的方程带入计算
怎么构建新的解呢?
我们目前有两个方程,一个是由前面的方程合并来的,我们称它为方程1
另一个是我们这一步要合并的方程,称为方程2
我们利用
现在的关键就在于如何合并
我们知道,
(因为
我们就可以得出一个新的方程了
#include<iostream>
#include<cstdio>
#include<cstring>
#include<algorithm>
#define rii register int i
#define rij register int j
#define int long long
using namespace std;
int a[100005],b[100005],n,cj,ans,mod;
int exgcd(int a,int b,int &x,int &y)
{
if(b==0)
{
x=1;
y=0;
return a;
}
int gcd=exgcd(b,a%b,x,y);
int zcq=x;
x=y;
y=zcq-a/b*y;
return gcd;
}
int pw(int l,int r,int p)
{
int an=0;
while(r>0)
{
if(r&1)
{
an=(an+l)%p;
}
l*=2;
l%=p;
r/=2;
}
return an;
}
signed main()
{
scanf("%lld",&n);
for(rii=1;i<=n;i++)
{
scanf("%lld%lld",&a[i],&b[i]);
}
cj=a[1];
ans=b[1];
int x,y;
for(rii=2;i<=n;i++)
{
int zcq=cj;
int st=a[i];
int ltt=(b[i]-ans%st+st)%st;
int gys=exgcd(zcq,st,x,y);//求解
int kkk=st/gys;
x=pw(x,ltt/gys,kkk);
ans+=cj*x;
cj*=kkk;
ans+=cj;
ans%=cj;//防溢出(同时确保答案最小)
}
cout<<ans;
//注意:此处已忽略乘法溢出(不用快速乘)
}
会了后别忘了写NOI2018屠龙勇士哦~
特别感谢luogu曹冲养猪和excrt题解作者,他们给了我很多思路
如果出锅,请私信联系(评论我不一定看的到),万分感谢
Latex在后台的预览中是正常的
但是在主界面就炸了
我也不知道为啥[苦笑]
有时间我会试着用图片代替掉Latex的
向大家说声抱歉了