今天上午效率挺高的,结果中午来了个亲戚,下午的效率难免低了些,不过,不会影响到我的信息学,呵呵
下面步入正题。
信息学
今天我们继续看数学相关。
lcm没有什么好讲的了,我们只要知道gcd(a,b)*lcm(a,b)==a*b
就可以了。
先来看扩展gcd
这个家伙至少有两个用途,一个是求解线性不定方程,另一个是求逆元。
显然求逆元是求解线性不定方程的一种特殊形式,我们放在求逆元里单独讲。
所以先来看看求解线性不定方程。
额,找了好久,洛谷上似乎没有什么好题,反倒是盯着一个叫偶数的题看了半天,发现那是一道求逆元的题,所以,我们待会再偶数那道题。
第一道:青蛙的约会
没错,还是洛谷,但我先是在POJ上找到这道题的,然后去洛谷搜的,权且用一下洛谷吧!(等知识点复习完,我就决定弃洛谷了,用POJ或者HDU或者BZOJ或者UOJ,这个回头再说)
下面看这道题,显然是一个数学题。
首先,我们设t时刻,两个小青蛙会相遇。
那么一定有如下等式:
y+n*t=x+m*t (mod L)
这是很显然的,两者走过的路程加上初始的坐标,再对L取模,就是他们当前坐标。
那么我们下面对这个式子进行变换,得:
(n-m)*t=x-y (mod L)
所以,也就等价于:
k*L+(n-m)*t=x-y
其中k是一个未知的整数。
那么,此时,一个活生生的不定方程就出现了!
显然直接套扩展gcd就行了。
但是别忘了判断有没有解,也就是gcd(n-m,L)|(x-y)是否成立
不成立就“不可能”
还有求出t以后别忘了把它调整成最小的正整数(不定方程的整数解有无限组)
所以代码如下:
#include<cstdio>
#include<cstring>
#include<algorithm>
#include<cmath>
using namespace std;
int x,y,m,n,L;
int exgcd(int a,int b,long long& x,long long& y){
if(b==0){
x=1,y=0;
return a;
}
int gcd=exgcd(b,a%b,y,x);
y-=x*(a/b);
return gcd;
}
int main(){
scanf("%d%d%d%d%d",&x,&y,&m,&n,&L);
int mnabs=n-m;
int xyabs=x-y;
long long k,t;
int gcd0=exgcd(L,mnabs,k,t);
if(xyabs%gcd0!=0){
printf("Impossible");
}else{
long long change=xyabs/gcd0;
t*=change;
long long delta=abs(L/gcd0);
while(t>0){
t-=delta;
}
while(t<0){
t+=delta;
}
printf("%d",t);
}
return 0;
}
里面还有一些数学知识,我就不多说了,自己翻翻不定方程的数学竞赛书就行了
我们下面来看看神奇的逆元。
求逆元有很多方法,而且逆元通常作为一个题的一部分出现,很少单独出现。
但是它真的单独出现过,而且是2012年提高组的题目,下面我们就来看一看这道题。
第二题:同余方程
没错就是赤裸裸地求逆元。
其实逆元就是ax+by=c这个不定方程在c=1的时候的特殊情形。
你也许会说,我们直接套一个扩展欧几里得就行了,但是我们好不容易逮到这个题,我们就来总结一下求逆元的多种方法。
方法一:扩展欧几里得
很简单,没什么值得说的,贴代码:
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
int a,b;
void exgcd(int aa,int bb,int& x,int& y){
if(bb==0){
x=1,y=0;
return;
}
exgcd(bb,aa%bb,y,x);
y-=x*(aa/bb);
}
int main(){
scanf("%d%d",&a,&b);
int xx,yy;
exgcd(a,b,xx,yy);
while(xx>0){
xx-=b;
}
while(xx<0){
xx+=b;
}
printf("%d",xx);
return 0;
}
方法二:费马小定理
没错就是费马小定理
也许你知道它可以用来求取模意义下的快速幂,但正是这个快速幂的算法也可以用来求逆元。
代码如下:
#include<cstdio>
#include<algorithm>
#include<cstring>
using namespace std;
long long a,b;
//mod为素数时,n为mod-2;mod为合数时,n为phi(mod)-1,phi为欧拉函数
long long pow_mod(long long x,long long n,long long mod){
long long res=1;
while(n>0){
if(n%2==1){
res=res*x%mod;
}
x=x*x%mod;
n/=2;
}
return res;
}
long long prime(long long n){
for(long long i=2;i*i<=n;i++){
if(n%i==0){
return 0;
}
}
return 1;
}
long long euler(long long n){
long long res=n,aa=n;
for(long long i=2;i*i<=n;i++){
if(aa%i==0){
res=res/i*(i-1);
while(aa%i==0){
aa/=i;
}
}
}
if(aa>1){
res=res/aa*(aa-1);
}
return res;
}
int main(){
scanf("%lld%lld",&a,&b);
long long n;
if(prime(b)){
n=b-2;
}else{
n=euler(b)-1;
}
printf("%lld",pow_mod(a,n,b));
return 0;
}
怎么使用,我在上面也写得很清楚了。
至于原理是什么,嘿嘿,没时间说了,大家自行百度。
既然提到了欧拉函数,那么我们就把欧拉函数的筛法也提一下:
void Init(){
euler[1]=1;
for(int i=2;i<Max;i++){
euler[i]=i;
}
for(int i=2;i<Max;i++){
if(euler[i]==i){
for(int j=i;j<Max;j+=i){
euler[j]=euler[j]/i*(i-1);//先进行除法是为了防止中间数据的溢出
}
}
}
}
和素数的筛法一样。
方法三:线性递推
这个方法仅仅是求逆元的方法,并不是本题的方法,因为本题数据太大,显然连数组都开不下,但我还是把代码贴在这:
//O(n)递推
const int N = 1e5 + 5;
int inv[N];
void inverse(int n, int p) {
inv[1] = 1;
for (int i=2; i<=n; ++i) {
inv[i] = 1LL* (p - p / i) * inv[p%i] % p;
}
}
其实有很多这样的打表方法,都会受到数组大小的限制,所以这类方法虽然很快,但往往很少有用武之地,但还是要备着,说不定某一天,它就能在战场上救了你的命。
这么简单的题,稍微拓展一点也挺有意思的,对吧?
下面我们不看数学了,下面让我们来看看竞赛大头——动态规划!
第三题:时间不够了
如题,我们就明天再看吧!
数学
今天看了三角函数,做了些题目。
那些变换公式,还是得记得非常熟练
记?
当然不是死记硬背
我们可以多用用,这样就可以记住了。
物理
今天又做了几道题(说过了,今天下午效率较低),还是很简单
昨天忘了一样东西,就是物理解题中的一个技巧——模型转换。
那是一道点电荷放在无限大的铁板前的题,求多长时间点电荷运动到板。
它就将点电荷的运动等效为天体的运动,然后巧妙解题。
这对于我们其实有不少借鉴意义(高考就算了,这么死板的考试,不可能出现这种方法的用武之地的)
总结
今天效率很高(请自觉忽略下午),希望明天继续保持!!