競技プログラミング必携アルゴリズムスニペット集

素因数分解


void factorize(int n, vector<int>& primes) {
    for (int d = 2; 1LL * d * d <= n; ++d) {
        if (n % d == 0) {
            primes.push_back(d);
            while (n % d == 0) n /= d;
        }
    }
    if (n > 1) primes.push_back(n);
}

ユークリッドの互除法による GCD / LCM


int gcd(int x, int y) { return y ? gcd(y, x % y) : x; }
long long lcm(long long x, long long y) { return x / gcd(x, y) * y; }

フェルマーの小定理を用いた逆元計算

mod が素数のとき、a / b mod p は a * b^(p-2) mod p と書ける。


const long long MOD = 19260817;

long long mod_pow(long long base, long long exp, long long mod) {
    long long res = 1;
    base %= mod;
    while (exp) {
        if (exp & 1) res = res * base % mod;
        base = base * base % mod;
        exp >>= 1;
    }
    return res;
}

long long inv(long long b) {
    return mod_pow(b, MOD - 2, MOD);
}

整数二分探索の 2 パターン


// 左側を残すパターン
int lower(int l, int r) {
    while (l < r) {
        int m = (l + r) / 2;
        if (check(m)) r = m;
        else l = m + 1;
    }
    return l;
}

// 右側を残すパターン
int upper(int l, int r) {
    while (l < r) {
        int m = (l + r + 1) / 2;
        if (check(m)) l = m;
        else r = m - 1;
    }
    return l;
}

座標圧縮 (Coordinate Compression)


vector<int> zip(vector<int> raw) {
    vector<int> xs = raw;
    sort(xs.begin(), xs.end());
    xs.erase(unique(xs.begin(), xs.end()), xs.end());
    for (int& v : raw)
        v = lower_bound(xs.begin(), xs.end(), v) - xs.begin();
    return raw;
}

二次元累積和


using ll = long long;
const int MAX = 1010;
ll s[MAX][MAX];

ll query(int x1, int y1, int x2, int y2) {
    return s[x2][y2] - s[x1-1][y2] - s[x2][y1-1] + s[x1-1][y1-1];
}

void build(int n, int m) {
    for (int i = 1; i <= n; ++i)
        for (int j = 1; j <= m; ++j) {
            cin >> s[i][j];
            s[i][j] += s[i-1][j] + s[i][j-1] - s[i-1][j-1];
        }
}

環状配分問題 (HAOI2008 チョコレート配布)

中央値を使って最小移動量を求める。


long long solve() {
    int n; cin >> n;
    vector<long long> a(n), c(n+1);
    long long sum = 0;
    for (int i = 0; i < n; ++i) {
        cin >> a[i];
        sum += a[i];
    }
    long long ave = sum / n;
    for (int i = 1; i <= n; ++i)
        c[i] = c[i-1] + a[i-1] - ave;
    sort(c.begin()+1, c.end());
    long long med = c[(n+1)/2], res = 0;
    for (int i = 1; i <= n; ++i)
        res += abs(c[i] - med);
    return res;
}

基数変換 (任意進数変換)


string convert(const string& num, int from, int to) {
    vector<int> digits;
    for (char c : num) {
        if (isdigit(c)) digits.push_back(c - '0');
        else if (isupper(c)) digits.push_back(10 + c - 'A');
        else digits.push_back(36 + c - 'a');
    }
    reverse(digits.begin(), digits.end());

    vector<int> res;
    while (!digits.empty()) {
        int rem = 0;
        for (int i = digits.size()-1; i >= 0; --i) {
            digits[i] += rem * from;
            rem = digits[i] % to;
            digits[i] /= to;
        }
        res.push_back(rem);
        while (!digits.empty() && digits.back() == 0) digits.pop_back();
    }
    reverse(res.begin(), res.end());

    string ans;
    for (int v : res) {
        if (v < 10) ans += '0' + v;
        else if (v < 36) ans += 'A' + v - 10;
        else ans += 'a' + v - 36;
    }
    return ans;
}

数論的ブロック (Floor Sum 高速化)


long long floor_sum(long long n, long long k) {
    long long res = n * k;
    for (long long l = 1, r; l <= n; l = r + 1) {
        r = k / l ? min(n, k / (k / l)) : n;
        res -= (k / l) * (r - l + 1) * (l + r) / 2;
    }
    return res;
}

ディリクレ前処理 (Dirichlet Prefix Sum)


void dirichlet(vector<unsigned>& f) {
    int n = f.size() - 1;
    vector<bool> vis(n+1, false);
    for (int i = 1; i <= n; ++i) {
        if (vis[i]) continue;
        for (int j = 1; i * j <= n; ++j) {
            f[i*j] += f[j];
            vis[i*j] = true;
        }
    }
}

区間マージ


bool merge(const vector<pair<int,int>>& segs) {
    vector<pair<int,int>> tmp = segs;
    sort(tmp.begin(), tmp.end());
    int ed = tmp[0].second;
    for (size_t i = 1; i < tmp.size(); ++i) {
        if (tmp[i].first <= ed) ed = max(ed, tmp[i].second);
        else return false;
    }
    return true;
}

多路マージ (優先度付きキュー)


int fishing(int n, int T, vector<int> a, vector<int> d, vector<int> t) {
    priority_queue<pair<int,int>> pq;
    for (int i = 0; i < n; ++i)
        pq.emplace(a[i], i);
    int res = 0;
    for (int step = 0; step < T; ++step) {
        auto [val, id] = pq.top(); pq.pop();
        res += val;
        val = max(0, val - d[id]);
        pq.emplace(val, id);
    }
    return res;
}

増加部分列の個数 (DP)


int count_lis(const string& s) {
    int n = s.size();
    vector<int> dp(n, 1);
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < i; ++j) {
            if (s[j] < s[i]) dp[i] += dp[j];
            else if (s[j] == s[i]) dp[j] = 0;
        }
    }
    return accumulate(dp.begin(), dp.end(), 0);
}

約数の総和 (高速等比級数和)


const int MOD = 9901;

int geo_sum(int p, int k) {
    if (k == 0) return 1;
    if (k % 2 == 0)
        return (1LL + 1LL * p * geo_sum(p, k-1)) % MOD;
    return 1LL * geo_sum(p, k/2) * (1 + pow_mod(p, k/2 + 1, MOD)) % MOD;
}

int sum_of_divisors(int n, int k) {
    int res = 1;
    for (int p = 2; p * p <= n; ++p) {
        if (n % p) continue;
        int cnt = 0;
        while (n % p == 0) { n /= p; ++cnt; }
        res = 1LL * res * geo_sum(p, cnt * k) % MOD;
    }
    if (n > 1) res = 1LL * res * geo_sum(n, k) % MOD;
    return res;
}

Fenwick Tree (Binary Indexed Tree)


template<typename T>
struct Fenwick {
    vector<T> bit;
    int n;
    Fenwick(int n) : n(n), bit(n+1) {}
    void add(int idx, T delta) {
        for (; idx <= n; idx += idx & -idx) bit[idx] += delta;
    }
    T sum(int idx) {
        T res = 0;
        for (; idx; idx -= idx & -idx) res += bit[idx];
        return res;
    }
    T range(int l, int r) { return sum(r) - sum(l-1); }
};

タグ: number-theory prime-factorization gcd-lcm modular-inverse binary-search

9月27日 15:18 投稿