banner
YangWithU

YangWithU

可能是第一个上链的 Acmer ?! 平时主要记录做题题解,希望我的文章可以启发到你!如有错误,欢迎指正!

算競常見問題合集

memset 遇到 TLE#

const int N = 1e8 + 10;
int a[N];

int main()
{
    int t; cin >> t;
    while (t--) {
        memset(a, 0, sizeof a);
        ...
    }
}

memset 函數會將整個陣列進行填充,但遇到多組資料的情況,每次都對 a 填充一次,顯然會 TLE

解決思路可以有兩種:

  • 捨棄全域變數

    捨棄全域變數帶來最大的困難是函數之間變數傳遞的問題,一般函數可以添加一個參數,但對於 dfs 來說較為不便,考慮使用 lambda 進行 dfs 的實現。

  • 捨棄 memset 換成 fill()assign()

    只要將不必要操作省去,操作次數在合理的範圍內滿足需要即可

筆者個人傾向捨棄全域變數的寫法。為實現我們這一操作,對一般函數,我們采用引用傳遞。下面簡析 recursive lambda 的寫法:

lambda 遞迴#

使用 self -- 最快#

auto dfs1 = [&](auto &self, int u, int p) -> int {
        int ret = 1;
        for(auto v : adj[u]) {
            if(v != p)
                ret += self(self, v, u);
        }
        return ret;
    };

利用 self 指代表達式自身,給 auto 的推導指明了方向,同時采用引用避免拷貝。

使用 std::function<> -- 最簡便#

該模板類用於包裝可以調用的任何類型的可調用對象(callable object),包括函數指針、函數對象、成員函數指針、Lambda 表達式等等

#include <functional>
#include <iostream>

void foo(int x) {
    std::cout << "foo(" << x << ")" << std::endl;
}

int main() {
    std::function<void(int)> f1 = foo;
    f1(42);
    
    std::function<void()> f2 = []() { std::cout << "lambda" << std::endl; };
    f2();
    
    struct Bar {
        void operator()(int x) const {
            std::cout << "Bar::operator()(" << x << ")" << std::endl;
        }
    };
    std::function<void(int)> f3 = Bar();
    f3(123);
    
    return 0;
}

原理在於向該類內部傳入函數。但大量遞迴,新開大量 std::function<> 存在較大開銷。

approach:

function<int(int, int)> dfs2 = [&](int u, int p) {
        int ret = 1;
        for(auto v : adj[u]) {
            if(v != p) {
                ret += dfs2(v, u);
            }
        }
        return ret;
    };

Benchmark#

int main() {
    int n; cin >> n;
    vector<vector<int>> adj(n);
    for(int i = 0; i < n - 1; i++) adj[i].push_back(i + 1);

    auto dfs1 = [&](auto &self, int u, int p) -> int {
        int ret = 1;
        for(auto v : adj[u]) {
            if(v != p)
                ret += self(self, v, u);
        }
        return ret;
    };

    function<int(int, int)> dfs2 = [&](int u, int p) {
        int ret = 1;
        for(auto v : adj[u]) {
            if(v != p) {
                ret += dfs2(v, u);
            }
        }
        return ret;
    };

    cout << fixed << setprecision(10);

    auto start = clock();
    int res1 = dfs1(dfs1, 0, -1);
    cout << res1 << '\n';
    cout << "Passing self: " << (double) (clock() - start) / CLOCKS_PER_SEC << '\n';

    start = clock();
    int res2 = dfs2(0, -1);
    cout << res2 << '\n';
    cout << "std::function: " << (double) (clock() - start) / CLOCKS_PER_SEC << '\n';

    assert(res1 == res2);
    return 0;
}

/* ---- results ---- */

n = 1e5
Passing self: 0.0000000000
std::function: 0.0180000000

n = 1e6
Passing self: 0.0310000000
std::function: 0.0780000000

n = 2e6
Passing self: 0.0620000000
std::function: 0.1750000000

待續...#

載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。