关于CPP算法竞赛中的快读、快写

本文介绍了在C++算法竞赛中,利用fread和fwrite进行快速读写字符和整数的方法,以及封装成iof::ifast和ofast类,实现在输入输出时提高性能,特别是在洛谷P9027问题中的应用,提高了约20%-30%的运行速度。
摘要由CSDN通过智能技术生成

#关于CPP算法竞赛中的快读、快写

作者:成都理工大学 胡鑫

前言

一些事实
  • 在C++中,常见的读写速度 std::cin std::cout < scanf() printf() < getchar() putchar() < fread() fwrite()。(当然 加速 之后的std::cin std::cout的速度与scanf() printf()不相上下)

  • 大多数的OJ都能通过fread() fwrite()读写数据。

平时在刷题或比赛的时候,会出现一些题,虽然写出了正解,但仍会卡掉你的某一两个测试点,然后用快读快写之后,就AC了。

为什么快读会这么快?

因为读入输出字符比读入输出数字快许多,所以这里介绍基于fread() fwrite()的快读、快写方法及实现。

快读的实现

如何用fread()getchar()

fread函数

(参考https://cplusplus.com/reference/cstdio/fread/

size_t fread(void *restrict buffer, size_t size, size_t count, FILE *restrict stream);

其中buffer是读取数据的字符串,size是一次读入的个数,count是读入的次数,stream是输入流。

为了接收fread()函数的输入,我们定义了:

char buffer[0xfffff], *front = buffer, *back = buffer;

由此重写getchar()函数:

char getchar() {
    if (front == back) {
        back = buffer + fread(buffer, 1, 0xfffff, stdin);
        front = buffer;
    }
    return *(front++);
}

用重写的getchar()读入一个整型

' ','\n'为分割,按位读取一个整数:

void read(int &x) {
    char c;
    do {
        c = getchar();
    } while (c == ' ' || c == '\n');
    x = 0;
    int w = 1;
    if (c == '-') {
        w = -1;
        c = getchar();
    }
    do {
        x = (x << 1) + (x << 3) + c - '0';
        c = getchar();
    } while (c != ' ' && c != '\n');
    x *= w;
}

这样,就完成了快读操作的实现。

快写的实现

如何用fwriteputchar()

fwrite函数

(参考https://en.cppreference.com/w/c/io/fwrite

size_t fwrite(const void *buffer, size_t size, size_t count, FILE *stream);

fwrite()相同,buffer是读取数据的字符串,size是一次读入的个数,count是读入的次数,stream是输入流。不过不同的是,我们可以一次性输出所有的数据。
于是我们定义:

char *top, buffer[0xfffff];

由此我们又可以重写putchar()函数:

void putchar(char x) {
    if (top - buffer < 0xfffff) {
        *top++ = x;
    } else {
        fwrite(buffer, top - buffer, 1, stdout);
        top = buffer;
        *top++ = x;
    }
}

当然,每执行一次函数就会判断一次top - buffer < 0xfffff,所以我们手动定义一个刷新函数void flush(),然后在缓冲区满和程序结束后刷新。

void flush() {
    fwrite(buffer, top - buffer, 1, stdout);
    top = buffer;
}

用重写的putchar()来输出一个整型

void print(const int &x) {
    if (x > 9) {
        print(x / 10);
    }
    putchar(x % 10 + '0');
}

由此我们完成了快写的实现。

快读快写的封装类

既然我们已经完成了快读,快写的实现,那我们不妨效仿我们熟悉的std::cin std::out<<,>>符号的重载,将上述的快读快写封装为一个C++类。

代码如下:

namespace iof {
    const int Buffer_MAXSIZE = 0xfffff;

    const char endl = '\n';

    class ifast {
    public:
        ifast &operator>>(char &ch);

        ifast &operator>>(char *s);

        ifast &operator>>(std::string &string);

        ifast &operator>>(bool &x) { return read_unsigned_integer(x); }

        ifast &operator>>(int &x) { return read_signed_integer(x); }

        ifast &operator>>(long long &x) { return read_signed_integer(x); }

        ifast &operator>>(unsigned int &x) { return read_unsigned_integer(x); }

        ifast &operator>>(unsigned long long &x) { return read_unsigned_integer(x); }

        void getline(std::string &string);

        void getline(char *s);

        auto hasMoreToken() { return front <= back; }

    private:
        template<typename T>
        ifast &read_signed_integer(T &x);

        template<typename T>
        ifast &read_unsigned_integer(T &x);

        char getchar();

        char buffer[Buffer_MAXSIZE]{};

        char *front = buffer;

        char *back = buffer;

        char next_char{};
    } fin;

    class ofast {
    public:
        ofast &operator<<(const char &c);

        ofast &operator<<(const char *c);

        ofast &operator<<(const std::string &s);

        ofast &operator<<(const bool &x) { return print_integer(x); }

        ofast &operator<<(const int &x) { return print_integer(x); }

        ofast &operator<<(const long long &x) { return print_integer(x); }

        ofast &operator<<(const unsigned int &x) { return print_integer(x); }

        ofast &operator<<(const unsigned long long &x) { return print_integer(x); }

        void flush();

        ~ofast() { flush(); }

    private:
        void putchar(const char &c) { *top++ = c; }

        char buffer[Buffer_MAXSIZE]{};

        char *top = buffer;

        template<typename T>
        ofast &print_positive_integer(const T &x);

        template<typename T>
        ofast &print_integer(const T &x);
    } fout;

    char ifast::getchar() {
        if (front == back) {
            back = buffer + fread(buffer, 1, Buffer_MAXSIZE, stdin);
            front = buffer;
        }
        return *(front++);
    }

    void ifast::getline(std::string &string) {
        string.clear();
        for (*this >> next_char; hasMoreToken() && next_char != '\n'; next_char = getchar()) {
            string.push_back(next_char);
        }
    }

    void ifast::getline(char *s) {
        for (char *p = s; hasMoreToken() && next_char != '\n'; next_char = getchar(), p++) {
            *p = next_char;
        }
    }

    ifast &ifast::operator>>(char &ch) {
        do {
            ch = getchar();
        } while (hasMoreToken() && (ch == ' ' || ch == '\n'));
        return *this;
    }

    ifast &ifast::operator>>(char *s) {
        *this >> next_char;
        for (char *p = s; hasMoreToken() && next_char != ' ' && next_char != '\n'; next_char = getchar(), p++) {
            *p = next_char;
        }
        return *this;
    }

    ifast &ifast::operator>>(std::string &string) {
        *this >> next_char;
        string.clear();
        for (; hasMoreToken() && next_char != ' ' && next_char != '\n'; next_char = getchar()) {
            string.push_back(next_char);
        }
        return *this;
    }

    template<typename T>
    ifast &ifast::read_unsigned_integer(T &x) {
        *this >> next_char;
        x = 0;
        do {
            x = (x << 1) + (x << 3) + next_char - '0';
            next_char = getchar();
        } while (hasMoreToken() && next_char != ' ' && next_char != '\n');
        return *this;
    }

    template<typename T>
    ifast &ifast::read_signed_integer(T &x) {
        *this >> next_char;
        auto isNegative = next_char == '-';
        if (isNegative) {
            next_char = getchar();
        }
        x = 0;
        do {
            x = (x << 1) + (x << 3) + next_char - '0';
            next_char = getchar();
        } while (hasMoreToken() && next_char != ' ' && next_char != '\n');
        if (isNegative) {
            x = -x;
        }
        return *this;
    }

    ofast &ofast::operator<<(const char &c) {
        putchar(c);
        return *this;
    }

    ofast &ofast::operator<<(const char *c) {
        for (auto *p = c; *p != '\0'; p++) {
            putchar(*p);
        }
        return *this;
    }

    ofast &ofast::operator<<(const std::string &s) {
        for (const auto &i: s) {
            putchar(i);
        }
        return *this;
    }

    void ofast::flush() {
        fwrite(buffer, 1, top - buffer, stdout);
        top = buffer;
    }

    template<typename T>
    ofast &ofast::print_integer(const T &x) {
        if (x > 0) {
            return print_positive_integer(x);
        } else if (x == 0) {
            putchar('0');
            return *this;
        } else {
            putchar('-');
            return print_positive_integer(-x);
        }
    }

    template<typename T>
    ofast &ofast::print_positive_integer(const T &x) {
        if (x > 9) {
            print_positive_integer(x / 10);
        }
        putchar(x % 10 + '0');
        return *this;
    }
}

注意事项

  • ifast, ofast不能用于读写浮点数

  • 该快读类无法与其他的输入输出类型同时使用

  • ofast的缓冲上限是0xfffff,如果输出过长需要手动刷新

实际应用

洛谷P9027为例:

#include <iostream>
#include <cmath>

//...

using namespace std;
using namespace iof;

int n, m;

int a[200010], stf[200010][20], q[200010][3], f[200010][20];;

int gcd(int x, int y) {
    return y ? gcd(y, x % y) : x;
}

int lcm(int x, int y) {
    return x * y / gcd(x, y);
}

int query(int l, int r) {
    int k = log2(r - l + 1);
    return gcd(stf[l][k], stf[r - (1 << k) + 1][k]);
}

signed main() {
    fin >> n >> m;

    for (int i = 1; i <= m; i++) {
        int l, r, x;
        fin >> l >> r >> x;
        q[i][0] = l;
        q[i][1] = r;
        q[i][2] = x;
        f[l][x]++, f[r + 1][x]--;
    }

    for (int i = 1; i <= n; i++) {
        int k = 1;
        for (int j = 1; j <= 16; j++) {
            f[i][j] += f[i - 1][j];
            if (f[i][j]) k = lcm(k, j);
        }
        a[i] = k;
    }

    for (int len = 0; len < 20; len++) {
        for (int i = 1; i + (1 << len) - 1 <= n; i++) {
            if (!len) {
                stf[i][len] = a[i];
            } else {
                stf[i][len] = gcd(stf[i][len - 1], stf[i + (1 << (len - 1))][len - 1]);
            }
        }
    }

    for (int i = 1; i <= m; i++) {
        if (query(q[i][0], q[i][1]) != q[i][2]) {
            fout << "Impossible";
            return 0;
        }
    }

    for (int i = 1; i <= n; i++) {
        fout << a[i] << ' ';
    }

    return 0;
}

这是io加速后的std::cin std::cout的速度:
1

而用了快读的速度:
1

可以看到明显快了20%-30%的速度。

后记

如有不足,请大佬在评论区指出!

最后祝看到这篇文章的朋友都能ACM金!

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值