1. 程式人生 > >【學術篇】SDOI2009 SuperGCD

【學術篇】SDOI2009 SuperGCD

而已 一個 有一個 優化 [1] == code 依賴 size

特別說明: 為了避免以後搬家時的麻煩, 這裏的文章繼續沿用csdn的風格和分類好了~

Emmmm這個題是一道高精度的模板題啊~
既然是高精度的裸題, 那我們這些懶人當然是選擇:用python啦~

你看這不就做完了麽←_←

a=input()
b=input()
while(b):
    c=b
    b=a%b
    a=c
print a

當然這份代碼並不能在luogu上AC 應該是數據出鍋了.
(當然也不能算是出鍋, 只是不太符合題目中說的輸入格式而已...刻意卡python
(當然還是有一些python黨摸清了讀入的規律然後卡過去了 好像rank1就是python吧

不過畢竟是一道省選題, 如果省選真的碰到了總不能用python寫吧(說好的遇到高精的題分就不要了呢


而且還可以鍛煉碼力(最後發現並沒有
所以決定用c++寫一發.

而作為STL依賴癥的重度患者, 這次的代碼直接上了一份

typedef vector<int> bignum;

不過其實是很方便的_(:з」∠)_ vector莫名其妙的實用, 經常跑得比數組還快, 不知道是怎麽做到的.

然後如果真的按照上面python的代碼, 那我們要寫一個高精取模. 然而我並不會寫.
這時候就需要用到我們中華民族老祖宗的智慧了——"更相減損之術"!!!

不過單純的不優化的更相減損之術是很慢滴, 親測在不壓位的情況下使用可以得到40pts.

我們可以考慮一個簡單易行的, 看上去也很顯然的方法:
我們進行一波分類討論:

  • \(a,b\)都是偶數, 那麽\(gcd(a,b)=2gcd(\frac a2,\frac b2)\);
  • \(a,b\)中有且只有一個偶數, 假如是\(a\), 那麽\(gcd(a,b)=gcd(\frac a2,b)\), 是\(b\)的話同理;
  • \(a,b\)都是奇數, 那就更相減損, \(gcd(a,b)=gcd(a-b,b)\).

這樣的話我們叠代一波(註意不要遞歸, 遞歸超級損效率)就可以做了. 我們要做的操作有:

  1. 輸入
  2. 判斷奇偶
  3. 減法
  4. 除2
  5. 乘2
  6. 輸出
    這些都不是很難.. 我們分別做一下就好了.
    這樣親測我們已經能拿到70pts了.

那剩下30pts怎麽找呢? 壓位啊~
沒有試壓更少的位, 但是壓4位已經可以跑得過了...
壓8位的效率似乎提高了一丟丟, 但是還是挺慢的..
一定是我這個人自帶大常數, 無數事實都證明了這一點(明明是你整天用STL...

然後就是代碼了, 可以看到裏面有很多實現是挺愚蠢的...(要不然為啥跑得慢呢←_←

這是壓8位的

// luogu-judger-enable-o2
#include <cstdio>
#include <string>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e8;
typedef vector<int> bignum;
inline int gcd(int a, int b) {
    if (!b) return a;
    return gcd(b, a%b);
}
inline bignum read() {
    bignum a; string s, ss; cin >> s; reverse(s.begin(),s.end());
    while (s.size() > 8) {
        ss = s.substr(0, 8);
        //a.push_back(ss[0] + ss[1] * 10 + ss[2] * 100 + ss[3] * 1000 - 48 * 1111);
        int t = 1, tt = 0;
        for (int i = 0; i < 8; ++i, t *= 10)
            tt += (ss[i] - 48)*t;
        a.push_back(tt);
        s = s.substr(8, s.size());
    } int b = 0; reverse(s.begin(), s.end());
    for (int i = 0; i < s.size(); ++i)
        b = b * 10 + s[i] - 48;
    a.push_back(b);
    return a;
}
inline void output(bignum a) {
    int i = a.size() - 1;
    printf("%d", a[i]);
    if (!i) return;
    for (--i; i >= 0; --i)
        printf("%08d", a[i]);
}
inline bool iseven(bignum a) {
    return !(*a.begin() & 1);
}
inline bool iszero(bignum a) {
    return a.size() == 1 && *a.begin() == 0;
}
inline bool bigger(bignum a, bignum b) {
    if (a.size() > b.size()) return 1;
    if (a.size() < b.size()) return 0;
    for (int i = a.size() - 1; i >= 0; --i) {
        if (a[i] > b[i]) return 1;
        if (a[i] < b[i]) return 0;
    }
    return 0;
}
inline void delzero(bignum& a) {
    while (a.size() > 1) {
        bignum::iterator it = --a.end();
        if (*it) break;
        a.erase(it);
        it = --a.end();
    }
}
inline void sub(bignum &a, bignum b) {
    int i;
    for (i = 0; i < b.size(); ++i) {
        if (a[i] < b[i])
            --a[i + 1], a[i] += N;
        a[i] = a[i] - b[i];
    }
    while (i < a.size() && a[i] < 0)
    {
        a[i] += N;
        ++i;
        --a[i];
    }
    delzero(a);
}
inline void half(bignum &a) {
    for (int i = a.size() - 1; i >= 0; --i) {
        if (a[i] & 1)
            a[i - 1] += N;
        a[i] = a[i] >> 1;
    }
    delzero(a);
}
inline void doubled(bignum& a) {
    for (int i = 0; i < a.size(); ++i) {
        a[i] <<= 1;
        if (i > 0 && a[i - 1] >= N) a[i - 1] -= N, ++a[i];
    }
    bignum::iterator it = --a.end();
    if (*it >= N) *it -= N, a.push_back(1);
}
int main() {
    bignum a = read(), b = read(); int c = 0;
    while (1) {
        if (bigger(b, a)) swap(a, b);
        if (iszero(b)) break;
        if (iseven(a) && iseven(b)) {
            half(a); half(b); ++c;
        }
        else if (iseven(a)) half(a);
        else if (iseven(b)) half(b);
        else sub(a, b);
    }
    for (; c; --c) 
        doubled(a);
    output(a);
}

這是壓4位的(其實差不多, 大約只有輸入輸出改了的樣子)

#include <cstdio>
#include <string>
#include <vector>
#include <iostream>
#include <algorithm>
using namespace std;
const int N = 1e4;
typedef vector<int> bignum;
inline int gcd(int a, int b) {
    if (!b) return a;
    return gcd(b, a%b);
}
inline bignum read() {
    bignum a; string s, ss; cin >> s; reverse(s.begin(),s.end());
    while (s.size() > 4) {
        ss = s.substr(0, 4);
        a.push_back(ss[0] + ss[1] * 10 + ss[2] * 100 + ss[3] * 1000 - 48 * 1111);
        s = s.substr(4, s.size());
    } int b = 0; reverse(s.begin(), s.end());
    for (int i = 0; i < s.size(); ++i)
        b = b * 10 + s[i] - 48;
    a.push_back(b);
    return a;
}
inline void output(bignum a) {
    int i = a.size() - 1;
    printf("%d", a[i]);
    if (!i) return;
    for (--i; i >= 0; --i)
        printf("%04d", a[i]);
}
inline bool iseven(bignum a) {
    return !(*a.begin() & 1);
}
inline bool iszero(bignum a) {
    return a.size() == 1 && *a.begin() == 0;
}
inline bool bigger(bignum a, bignum b) {
    if (a.size() > b.size()) return 1;
    if (a.size() < b.size()) return 0;
    for (int i = a.size() - 1; i >= 0; --i) {
        if (a[i] > b[i]) return 1;
        if (a[i] < b[i]) return 0;
    }
    return 0;
}
inline void delzero(bignum& a) {
    while (a.size() > 1) {
        bignum::iterator it = --a.end();
        if (*it) break;
        a.erase(it);
        it = --a.end();
    }
}
inline void sub(bignum &a, bignum b) {
    int i;
    for (i = 0; i < b.size(); ++i) {
        if (a[i] < b[i])
            --a[i + 1], a[i] += N;
        a[i] = a[i] - b[i];
    }
    while (i < a.size() && a[i] < 0)
    {
        a[i] += N;
        ++i;
        --a[i];
    }
    delzero(a);
}
inline void half(bignum &a) {
    for (int i = a.size() - 1; i >= 0; --i) {
        if (a[i] & 1)
            a[i - 1] += N;
        a[i] = a[i] >> 1;
    }
    delzero(a);
}
inline void doubled(bignum& a) {
    for (int i = 0; i < a.size(); ++i) {
        a[i] <<= 1;
        if (i > 0 && a[i - 1] >= N) a[i - 1] -= N, ++a[i];
    }
    bignum::iterator it = --a.end();
    if (*it >= N) *it -= N, a.push_back(1);
}
int main() {
    bignum a = read(), b = read(); int c = 0;
    while (1) {
        if (bigger(b, a)) swap(a, b);
        if (iszero(b)) break;
        if (iseven(a) && iseven(b)) {
            half(a); half(b); ++c;
        }
        else if (iseven(a)) half(a);
        else if (iseven(b)) half(b);
        else sub(a, b);
    }
    for (; c; --c) 
        doubled(a);
    output(a);
}

嗯, 就這樣吧, 完結撒花??ヽ(°▽°)ノ?

【學術篇】SDOI2009 SuperGCD