1. 程式人生 > >清北學堂 DAY1

清北學堂 DAY1

預處理 .com pan 總結 組合數 pac style 影響 math

今天是李昊老師的講授~~

總結了一下今天的內容:

1.高精度算法


(1) 高精度加法

技術分享圖片

思路:模擬豎式運算

註意:進位

優化:壓位

程序代碼:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<cstdlib>
using namespace std;
char a1[1000],b1[1000];
int a[1000],b[1000],c[1000];
int main(){
scanf("%s",a1);

scanf("%s",b1);
int lena=strlen(a1);
for(int i=lena-1;i>=0;i--)a[lena-i]=a1[i]-‘0‘;
int lenb=strlen(b1);
for(int i=lenb-1;i>=0;i--)b[lenb-i]=b1[i]-‘0‘;
int lenc=max(lena,lenb);
for(int i=1;i<=lenc;i++)c[i]=a[i]+b[i]; //先將每一對應位加起來
for(int i=1;i<=lenc;i++){
c[i+1]+=c[i]/10; //進位
c[i]%=10;
}
while(c[lenc+1]>0) lenc+=1; //如果位數增多,則lenc++
for(int i=lenc;i>0;i--)
cout<<c[i];
return 0;
}

考慮負數的情況:

若只有一個負數,那麽就成為正加數-另一個加數的形式;

若有兩個負數,那麽先算兩個數的絕對值的和,再加上個負號‘-’;

(2) 高精度減法

思路:模擬豎式運算,考慮進位

註意:結果為0的情況

程序代碼:

#include<iostream>
#include<algorithm>

#include<cstdio>
#include<cmath>
#include<cstring>
#include<cstdlib>
using namespace std;
char a1[1000],b1[1000];
int a[1000],b[1000],c[1000];
int main(){
scanf("%s",a1);
scanf("%s",b1);
int lena=strlen(a1);
for(int i=lena-1;i>=0;i--)a[lena-i]=a1[i]-‘0‘;
int lenb=strlen(b1);
for(int i=lenb-1;i>=0;i--)b[lenb-i]=b1[i]-‘0‘;
int lenc=max(lena,lenb);
for(int i=1;i<=lenc;i++)c[i]=a[i]-b[i]; //先將每一對應位都相減,方便借位處理
for(int i=1;i<=lenc;i++){
if(c[i]<0) //若不夠0,就向高位借位+10,高位--
{
c[i]+=10;
c[i+1]--;
}
}
while(c[lenc]==0) lenc--; //除去前導0
for(int i=lenc;i>0;i--)
cout<<c[i];
return 0;
}

考慮負數的情況:

若只有一個負數:

<1>負數-正數 轉化為兩數絕對值相加,然後在前面加個負號‘-’;

<2>正數-負數 轉化為兩數絕對值相加;

若有兩個負數:

轉化為被減數的絕對值-減數的絕對值;

!!!小數減大數de處理方法:

用大數減小數,然後在前面加上負號‘-’;

(3) 高精度乘法

技術分享圖片

思路:模擬豎式運算,考慮進位

註意:結果為0的情況

程序代碼:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<cstdlib>
using namespace std;
char a1[1000],b1[1000];
int a[1000],b[1000],c[1000];
int main(){
scanf("%s",a1);
scanf("%s",b1);
int lena=strlen(a1);
for(int i=lena-1;i>=0;i--)a[lena-i]=a1[i]-‘0‘;
int lenb=strlen(b1);
for(int i=lenb-1;i>=0;i--)b[lenb-i]=b1[i]-‘0‘;
int lenc;
for(int i=1;i<=lena;i++)
for(int j=1;j<=lenb;j++)
c[i+j-1]+=a[i]*b[j]; //對應位相乘
for(int i=1;i<lena+lenb;i++)
{
c[i+1]+=c[i]/10; //進位
c[i]%=10;
}
lenc=lena+lenb-1;
while(c[lenc+1]>0) lenc++; //如果位數增多,lenc++
for(int i=lenc;i>0;i--)
cout<<c[i];
return 0;
}

考慮負數的情況:

若有一個負數: 正常絕對值相乘,前面加負號‘-’;

若有兩個負數: 正常絕對值相乘;

(4) 高精度除法 ——高精除單精

思路:模擬豎式運算

程序代碼:

#include<iostream>
#include<algorithm>
#include<cstdio>
#include<cmath>
#include<cstring>
#include<cstdlib>
using namespace std;
char a1[1000];
int a[1000],b[1000],c[1000],b1;
int main(){
scanf("%s",a1);
cin>>b1;
int lena=strlen(a1);
for(int i=lena-1;i>=0;i--)a[lena-i]=a1[i]-‘0‘;
for(int i=lena;i>0;i--){
c[i]=a[i]/b1;
a[i-1]+=(a[i]%b1)*10; 第lena位除以b1後的余數*10+第lena-1位的數繼續除
}
while(c[lena]==0 && lena>0)lena--;
for(int i=lena;i>0;i--)printf("%d",c[i]);

return 0;
}

2.模意義下運算

例:

在模7意義下的運算:

3*3=9≡2 (mod 7)

4+5=9≡2 (mod 7)

4-5=-1≡6 (mod 7)

註意:無除法運算

那碰到除法的怎麽辦呢???

假設a*b=t(mod p):

我們都知道費馬小定理:

如果p是一個質數,而整數a不是p的倍數,則有a^(p-1)≡1(mod p)

t*a^(p-2)≡b (mod p)

t/a≡b (mod p)

so 模意義下/a相當於*a^(p-2)

模意義下運算的性質:

1.滿足基本的交換律,分配率,結合律;

2.對中間結果取模不影響最終答案;

3.快速冪

首先讓我們思考一下怎麽求a^b%p?

有兩種求法:

分治

技術分享圖片

簡單說一下,就是要求a^b,那麽我們就求a^(b/2)再平方就好啦,求a^(b/2)同理

快速冪

技術分享圖片

4.費馬小定理

如果p是一個質數,而整數a不是p的倍數,則有a^(p-1)≡1(mod p)

應用:

計算組合數C(n,m)%(10^9+7)

C(n,m)=n!/((n-m)!*m!)

=n!*((n-m)!*m!)^(p-2)

=n!*(((n-m)!)^(p-2)*(m!)^(p-2))

所以我們只要預處理任意n!,(n!)^(p-2)就好了

技術分享圖片

清北學堂 DAY1