来自:https://blog.csdn.net/litble/article/details/75807726
写的很强
一开始觉得这东西好难。。。后来发现好像也没有这么难。。。
我们来看一道题目:
树王种了一棵treap,她现在决定把这棵treap改造为一棵无旋多叉triep,于是她摘下了treap的所有节点,发现如果她把节点3个3个一打包,会剩下2个节点。如果她把节点5个5个一打包,会剩下3个节点,如果把节点7个7个一打包,会剩下2个节点,求这棵treap最少有多少节点?
首先假如我们求出这样三个数k1,k2,k3,满足k1与3互质且是5和7的倍数,k2与5互质且是3,7的倍数,k3与7互质且是3和5的倍数,那么容易意会得到,k1∗2+k2∗3+k3∗2一定会是一个满足题目条件的数。而题目的通解可表示为这个数每次都加上3,5,7的最小公倍数
辣么如何求出k1,k2,k3呢?
首先我们求出3,5,7的lcm=105
然后我们令:
x1=105/3=35
x2=105/5=21
x3=105/7=15
然后我们求解以下方程:
a∗x1%3=1 //a×x1是k1,a×x1%3=1,那么2×a×x1%3=2与题意相符
b∗x2%5=1
c∗x3%7=1
啊呀这个格式的式子好眼熟。。。那么就愉快地用扩展欧几里德求出来吧!
a=2,b=1,c=1。
答案就是:
ans=(a∗x1∗2+b∗x2∗3+c∗x3∗2)%lcm=23
多棒啊!可爱的代码也很好写啊~
Code:
自己敲的完整代码
#include <iostream>
#include<cstring>
#include<algorithm>
#define ll long long
using namespace std;
ll m[15],a[15];
ll ex_gcd(ll a,ll b,ll &x,ll &y) //扩展gcd得到的x即为特解
{
if(!b)
{
x=1;
y=0;
return a;
}
ll r=ex_gcd(b,a%b,x,y);
ll t=y;
y=x-(a/b)*y;
x=t;
return r;
}
ll china(int n,ll *m,ll *a) //中国剩余定理
{
ll lcm=1,y,x,ans=0;
for(int i=0;i<n;i++)
lcm*=m[i];
for(int i=0;i<n;i++)
{
ll w=lcm/m[i];
ex_gcd(w,m[i],x,y); //a∗x1%3=1已知x1求a
x=(x%m[i]+m[i])%m[i]; //防止求出来的特解是负的
ans=(ans+a[i]*x*w)%lcm; //ans=(a∗x1∗2+b∗x2∗3+c∗x3∗2)%lcm=23
}
return ans;
}
int main()
{
int n;
cin>>n;
for(int i=0;i<n;i++)
{
cin>>m[i]>>a[i];
}
cout<<china(n,m,a);
return 0;
}