牛客競賽(gcd,快速冪)

1、最大公約數和最小公倍數問題

題目描述:

輸入2個正整數x0,y0(2<=x0<100000,2<=y0<=1000000),求出知足下列條件的P,Q的個數。node

條件:1.P,Q是正整數;
2.要求P,Q以x0爲最大公約數,以y0爲最小公倍數。c++

試求: 知足條件的全部可能的兩個正整數的個數。算法

輸入描述:

每一個測試文件包含不超過5組測試數據,每組兩個正整數x0和y0(2<=x0<100000,2<=y0<=1000000)。數組

輸出描述:

對於每組輸入數據,輸出知足條件的全部可能的兩個正整數的個數。函數

下面是對樣例數據的說明:測試

輸入3 60ui

此時的P Q分別爲:spa

3 60
15 12
12 15
60 3code

因此,知足條件的全部可能的兩個正整數的個數共4種。遞歸

#include<bits/stdc++.h>
using namespace std;
int gcd(int a,int b){
    if(b==0)
        return a;
    return gcd(b,a%b);
}
 
int main(){
    int x,y;
    while(~scanf("%d%d",&x,&y)){
        int ans=0;
        int mul=x*y;
        for(int i=x;i<=y;i++){
        if(mul%i!=0)
            continue;
        int k=mul/i;
        if(x==gcd(i,k))
            ans++;
    }
    cout<<ans<<endl;
    }
    return 0;
}

gcd(歐幾里得又稱展轉相除)算法

定理:兩個整數的最大公約數等於其中較小的那個數和兩數相除餘數的最大公約數。最大公約數(Greatest Common Divisor)縮寫爲GCD。
gcd(a,b) = gcd(b,a mod b) (不妨設a>b 且r=a mod b ,r不爲0)
證實 :a能夠表示成a = kb + r(a,b,k,r皆爲正整數,且r<b),則r = a mod b
假設d是a,b的一個公約數,記做d|a,d|b,即a和b均可以被d整除。
而r = a - kb,兩邊同時除以d,r/d=a/d-kb/d=m,由等式右邊可知m爲整數,所以d|r
所以d也是b,a mod b的公約數
假設d是b,a mod b的公約數, 則d|b,d|(a-k*b),k是一個整數。
進而d|a.所以d也是a,b的公約數
所以(a,b)和(b,a mod b)的公約數是同樣的,其最大公約數也必然相等,得證。

如下是gcd算法C++實現代碼:

int Gcd(int a, int b)
{
    if(b == 0)
        return a;
    return Gcd(b, a % b);
}

2、筱瑪愛地理

點擊查看題目描述

  • 這道看似簡單的題把好多人都卡死在了分數取模上
  • 賽後題解中涉及到我這個蒟蒻的不少知識盲區orz
  • 先上代碼再總結:
#include<bits/stdc++.h>
#define int long long
/*無論32位仍是64位的,int都是4個字節,一個字節8位,即32位。 
long long 都是8個字節,就是64位,這句話的做用就是將原來的int_32 型 改爲int_64型,
在一些預編譯裏面還會出現 #define _int64 long long的語句,c99標準以後,64位的int都用long long 來表示,這裏能夠理解爲處理標準兼容的問題。 */
#define N 500005
using namespace std; 
const int Mod=1e9+7;//科學計數法 
struct node{
    int x, y;
}a[N];//定義結構體的同時,定義大小爲N的結構體數組。 
inline bool cmp(node aa,node bb)
{
    return aa.y*bb.x>aa.x*bb.y; //避免精度問題因此採用交叉相乘的方式比較大小
} 
inline int ksm(int x,int y)//x是底數,y是指數
{//快速求模運算。 
    int ans1=1;
    while (y)
    {
        if (y&1) //逐位「與」運算,經過和1相與,保留最後一位 
        {
            ans1=1ll*ans1*x;
            ans1=ans1%Mod;//ans1記錄前面每次翻倍求模結果的乘積再求模
        }
          /*經過乘以1ll,將等號右邊的精度提升到long long ,低精度向高精度轉化,避免中間結果溢出範圍,右邊的ans如今是64位的int,原來32位的int型 10位數字,long long 19位數字, 1e+9是9位數字, 因此是32位仍是64位都不會超出範圍。 */
         y>>=1;//右移1位至關除以2,類比十進數,右移一位至關於除以10,同理左移,是乘以相應的進制基數。 
        x=1ll*x*x%Mod;//x中記錄每次翻倍的求模的結果。 
     }
     return ans1;
}
signed main()
{
    int n;
    scanf("%lld",&n);
    for (int i=1;i<=n;i++) 
    scanf("%lld%lld",&a[i].x,&a[i].y);//注意結構體的輸入方式。 
    sort(a+1,a+n+1,cmp);//i=1開始,因此起始位置爲a+1 
    for (int i=1;i<=n;i++) 
    printf("%lld\n",a[i].y*ksm(a[i].x,Mod-2)%Mod);//分數求模的定理轉換。 
    return 0;
}

(一)inline關鍵字

1.在 c/c++ 中,爲了解決一些頻繁調用的小函數大量消耗棧空間(棧內存)的問題,特別的引入了 inline 修飾符,表示爲內聯函數。
棧空間就是指放置程序的局部數據(也就是函數內數據)的內存空間。
在系統下,棧空間是有限的,假如頻繁大量的使用就會形成因棧空間不足而致使程序出錯的問題,如,函數的死循環遞歸調用的最終結果就是致使棧內存空間枯竭。
2.inline 的使用是有所限制的,inline 只適合涵數體內代碼簡單的函數使用,不能包含複雜的結構控制語句例如 while、switch,而且不能內聯函數自己不能是直接遞歸函數(即,本身內部還調用本身的函數)。
3.inline 函數僅僅是一個對編譯器的建議,因此最後可否真正內聯,看編譯器的意思,它若是認爲函數不復雜,能在調用點展開,就會真正內聯,並非說聲明瞭內聯就會內聯,聲明內聯只是一個建議而已。
慎用inline!!!
內聯是以代碼膨脹(複製) 爲代價,僅僅省去了函數調用的開銷,從而提升函數的執行效率。
若是執行函數體內代碼的時間,相比於函數調用的開銷較大,那麼效率的收穫會不多。另外一方面,每一處內聯函數的調用都要複製代碼,將使程序的總代碼量增大,消耗更多的內存空間。

(二)求模運算

  • 費馬小定理:對於質數p,任意整數a,均知足:\[a^p\equiv a(mod p)\]
  • 如下推導出分數求模
    • \[a^{p-1}modp\equiv1modp\]
    • \[a^{p-2}*amodp\equiv1modp\]
    • \[a^{p-2}modp\equiv\dfrac{1}{a}modp \]
    • \[a^{-n}modp\equiv a^{p-n-1}modp \]
  • 題中要求\(\beta =V/E\)\(\beta\)的模,便可轉化爲:\[\beta mod p=V*\dfrac{1}{e}modp\] (其中p=1e10+7)
  • 模運算具備如下性質:

    • \[(A+B)modp=(Amodp+Bmodp)modp\]
    • \[(A*B)modp=((Amodp)*(Bmodp))modp\]

  • 因此上式又可繼續轉化爲\(((Vmodp)*(e^{-1}modp))modp=V*(e^{p-2}modp)modp\)
    (由於V<p因此mod後不變)

    (三)按位與運算

    • 判斷奇偶通常我用的是取餘運算,今天學到還能夠按位與判斷奇偶
    • 按位與的優勢:效率更高,時間更快
    • 原理:
      • 按位與是將兩個數轉化爲二進制,若對應的位兩數都爲1,則結果中該位爲1,不然該位爲0
      • 一個數若是與1進行按位與運算,奇數轉化爲二進制後最後一位確定爲1,偶數確定爲0
      if((x&1)==1)
      printf("奇數");
      else if((x&1)==0)
      printf("偶數");
相關文章
相關標籤/搜索