JOISC 2014

D1T1 - Bus

この問題は、各クエリに最適なバスの乗車時間を計算するため、離線処理を活用します。乗車時間の最適化は、バスの到着時間を基準に、最遅乗車時間を求めることで達成されます。バスの到着時間をソートし、各バスの出発時間を管理します。クエリの処理には、バスの到着時間を基準にソートし、必要なバスのみを検索します。

時間計算の複雑度は、\( \mathcal{O}((N + M + Q) \log M) \)です。

struct BusInfo {
    int index;
    i64 arrival;
    BusInfo(int i, i64 a) : index(i), arrival(a) {}
    bool operator<(const BusInfo& b) const {
        return arrival == b.arrival ? index < b.index : arrival < b.arrival;
    }
};
int n, m, q;
i64 L[100001], ans[100001];
vector<BusInfo> buses, final_buses;
void solve() {
    // 入力読み込みと初期化
    for (int i = 0; i < m; ++i) {
        int a, b, x, y;
        // 入力
        buses.push_back(BusInfo(i, x));
        // 到着時刻の管理
    }
    sort(buses.begin(), buses.end(), [](const BusInfo& a, const BusInfo& b) {
        return a.arrival < b.arrival;
    });
    // 最終バスの管理
    for (int i = 0; i < buses.size(); ++i) {
        if (buses[i].index == n) {
            final_buses.push_back(buses[i]);
        }
    }
    // クエリの処理
    sort(final_buses.begin(), final_buses.end(), [](const BusInfo& a, const BusInfo& b) {
        return a.arrival > b.arrival;
    });
    // 結果計算
    for (int i = 0; i < q; ++i) {
        if (final_buses.empty()) {
            ans[i] = -1;
        } else {
            ans[i] = final_buses.back().arrival;
        }
    }
    // 結果出力
}

D1T2 - Growing Vegetables is Fun

この問題は、配列を単峰配列として構成し、逆序数を計算します。単峰配列の形成には、最大値を基準に左右に展開します。逆序数の計算には、BIT(Fenwick Tree)を用いて効率的に管理します。

時間計算の複雑度は、\( \mathcal{O}(N \log N) \)です。

struct FenwickTree {
    int size;
    vector<int> tree;
    FenwickTree(int s) : size(s), tree(s + 1, 0) {}
    void update(int index, int val) {
        while (index <= size) {
            tree[index] += val;
            index += index & -index;
        }
    }
    int query(int index) {
        int res = 0;
        while (index > 0) {
            res += tree[index];
            index -= index & -index;
        }
        return res;
    }
};
int n;
int a[100001], id[100001];
i64 result;
void solve() {
    // 入力読み込み
    // 配列のソート
    sort(id, id + n, [](int a, int b) {
        return a > b;
    });
    // BITの初期化
    FenwickTree ft(n);
    // 逆序数の計算
    for (int i = 0; i < n; ++i) {
        result += ft.query(id[i]);
        ft.update(id[i], 1);
    }
    // 結果出力
}

D1T4 - Ramen

この問題は、配列の要素を交互に比較し、最大値と最小値を分離します。交互比較の回数は、\( \lfloor \frac{N}{2} \rfloor \)です。

#include <vector>
#include <algorithm>
using namespace std;
const int MAX = 1003;
int left[MAX], right[MAX];
int numL = 0, numR = 0;
void Ramen(int N) {
    for (int i = 0; i < N; i += 2) {
        if (i + 1 < N) {
            if (Compare(i, i + 1) == 1) {
                left[numL++] = i;
                right[numR++] = i + 1;
            } else {
                left[numL++] = i + 1;
                right[numR++] = i;
            }
        }
    }
    if (numL == 0) {
        Answer(right[numR - 1], right[numR - 1]);
        return;
    }
    int maxL = left[0];
    for (int i = 1; i < numL; ++i) {
        if (Compare(left[i], maxL) == 1) {
            maxL = left[i];
        }
    }
    int minR = right[0];
    for (int i = 1; i < numR; ++i) {
        if (Compare(minR, right[i]) == 1) {
            minR = right[i];
        }
    }
    Answer(maxL, minR);
}

D2T2 - Making Friends is Fun

この問題は、グラフの連結性を管理します。各頂点の出次数と入次数を管理し、完全グラフの形成を検出します。検出された完全グラフの数を計算します。

時間計算の複雑度は、\( \mathcal{O}(N) \)です。

struct UnionFind {
    vector<int> parent, rank;
    int size;
    UnionFind(int s) : size(s), parent(s), rank(s, 1) {
        iota(parent.begin(), parent.end(), 0);
    }
    int find(int x) {
        if (parent[x] != x) {
            parent[x] = find(parent[x]);
        }
        return parent[x];
    }
    void unite(int x, int y) {
        x = find(x);
        y = find(y);
        if (x == y) return;
        if (rank[x] < rank[y]) swap(x, y);
        parent[y] = x;
        if (rank[x] == rank[y]) ++rank[x];
    }
};
int n, m;
vector<int> graph[100001];
bool visited[100001];
UnionFind uf(n);
void dfs(int u) {
    visited[u] = true;
    for (int v : graph[u]) {
        if (v != u) {
            uf.unite(u, v);
            if (!visited[v]) {
                dfs(v);
            }
        }
    }
}
i64 count = 0;
void solve() {
    // 入力読み込み
    // Union-Find初期化
    // グラフ構築
    // DFS実行
    // 結果計算
    for (int i = 0; i < n; ++i) {
        int root = uf.find(i);
        if (root == i) {
            count += uf.rank[i];
        }
    }
    // 結果出力
}

D2T3 - Stamp Rally

この問題は、各駅での決断を管理します。4つの決断を分析し、最適な道順を計算します。決断の管理には、動的計画法を用いて、未マッチの左括号数を管理します。

時間計算の複雑度は、\( \mathcal{O}(N^2) \)です。

int n;
i64 T;
int U[100001], V[100001], E[100001], D[100001];
i64 dp[100001][100001];
void solve() {
    // 入力読み込み
    // 初期化
    dp[0][0] = 0;
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j <= n; ++j) {
            // 状態遷移
            if (j > 0) {
                dp[i][j] = min(dp[i][j], dp[i - 1][j - 1] + V[i] + D[i]);
            }
            if (j < n) {
                dp[i][j] = min(dp[i][j], dp[i - 1][j + 1] + U[i] + E[i]);
            }
        }
    }
    // 結果計算
    // 結果出力
}

D3T1 - JOIOJI

この問題は、文字列の前処理を用いて、特定の文字組み合わせを検出します。前処理の結果を基準に、最適な区間を検出します。

時間計算の複雑度は、\( \mathcal{O}(N) \)です。

int n;
string s;
int preJ, preO, preI;
map<pair<int, int>, int> mp;
void solve() {
    // 初期化
    mp[{0, 0}] = 0;
    // 文字列前処理
    for (int i = 0; i < n; ++i) {
        if (s[i] == 'J') preJ++;
        if (s[i] == 'O') preO++;
        if (s[i] == 'I') preI++;
        // 状態管理
        auto key = make_pair(preJ - preO, preO - preI);
        if (mp.find(key) != mp.end()) {
            result = max(result, i - mp[key]);
        } else {
            mp[key] = i;
        }
    }
    // 結果出力
}

D3T2 - Scarecrows

この問題は、点の分布を管理します。点の分布を分析し、特定の条件を満足する点の数を計算します。

時間計算の複雑度は、\( \mathcal{O}(N \log^2 N) \)です。

struct Point {
    int x, y;
    Point(int xx, int yy) : x(xx), y(yy) {}
};
int n;
Point p[100001];
int sortedY[100001];
void solve() {
    // 入力読み込み
    // Y座標のソート
    sort(sortedY, sortedY + n);
    // 点のソート
    sort(p, p + n, [](const Point& a, const Point& b) {
        return a.x < b.x;
    });
    // 分割統治法
    // 結果計算
    // 結果出力
}

D4T1 - Constellation 2

この問題は、ベクトルの交差を管理します。ベクトルの交差を計算し、特定の条件を満足するベクトルの数を計算します。

時間計算の複雑度は、\( \mathcal{O}(N^2) \)です。

struct Vector2D {
    int x, y, c;
    Vector2D(int xx, int yy, int cc) : x(xx), y(yy), c(cc) {}
};
int n;
Vector2D v[100001];
i64 result = 0;
void solve() {
    // 入力読み込み
    // 初期化
    for (int i = 0; i < n; ++i) {
        for (int j = 0; j < n; ++j) {
            if (i == j) continue;
            // 交差計算
            result += computeCross(v[i], v[j]);
        }
    }
    // 結果出力
}

D4T3 - Straps

この問題は、特定の条件を満足する数列を管理します。数列の条件を満足するため、動的計画法を用いて最適解を計算します。

時間計算の複雑度は、\( \mathcal{O}(N^2) \)です。

int n;
int a[100001];
i64 b[100001];
i64 dp[100001][100001];
void solve() {
    // 初期化
    sort(a, a + n, greater<int>());
    // DP計算
    for (int i = 0; i <= n; ++i) {
        for (int j = 0; j <= n + 1; ++j) {
            dp[i][j] = max(dp[i][j], dp[i - 1][j]);
            // 状態遷移
        }
    }
    // 結果計算
    // 結果出力
}

タグ: JOISC Bus Growing Vegetables Ramen Making Friends

5月17日 17:06 投稿