2022 Benelux Algorithm Programming Contest (BAPC 22) A 、I、J、L

sleeeeeping發表於2024-05-10

A. Adjusted Average(暴力列舉+二分查詢)

分析

讀完題目可以發現k很小,那麼考慮暴力做法的時間複雜度為\(O(C_n^k)\),對於\(k\leq3\)的其實可以直接暴力創過去,但對於\(k=4\)的情況顯然不適用。那麼對應\(k=4\)的情況考慮最佳化,可以選擇將數分為兩個集合,先用一個set存下其中一個集合的所有選擇方案,此時列舉刪除另外一個集合中的哪兩個數,最後二分查詢最優的匹配方案。時間複雜度\(O(n^2logn)\)

程式碼實現

#include <bits/stdc++.h>
int main() {
    std::cin.tie(nullptr)->sync_with_stdio(false);
    #define int long long
    int n, k, x;
    std::cin >> n >> k >> x;
    std::vector<int> a(n);
    for (int i = 0; i < n; ++i) {
        std::cin >> a[i];
    }
    int sum = std::accumulate(a.begin(), a.end(), 0LL);
    double ans = abs((double)sum / n - x);
    for (int num = 1; num <= k; ++num) {
        std::set<int> st{(int)-1e18, (int)1E18};
        for (int i = 0; i < n; ++i) {
            ans = std::min(ans, abs((double)(sum - a[i]) / (n - 1) - x));
            if (num == 1) continue;
            for (int j = i + 1; j < n; ++j) {
                ans = std::min(ans, abs((double)(sum - a[i] - a[j]) / (n - 2) - x));
                if (num == 2) continue;
                int sur = sum - (n - num) * x - a[i] - a[j];
                auto it = st.lower_bound(sur);
                ans = std::min({ans, abs((double)(sur - *it) / (n - num)), abs((double)(sur - *(--it)) / (n - num))});
            }
            for (int j = 0; j < i; ++j) {
                st.emplace(a[i] + a[j] * (num == 4));
            }
            if (num == 3) st.emplace(a[i]);
        }
    }
    std::cout << ans << '\n';
}

原題在這裡(╹▽╹)

I - Imperfect Imperial Units(離散化)

分析

正解應該是從每個點開始搜一遍,但是寫的時候偷懶,於是魔改了一下Floyd創過去了。時間複雜度\(O(n^3 + q)\)

程式碼實現

#include <bits/stdc++.h>
int main() {
    std::cin.tie(nullptr)->sync_with_stdio(false);
    std::cout << std::setprecision(12);
    int n, m;
    std::cin >> n >> m;
    std::map<std::string, int> mp;
    std::vector<std::tuple<double, int, int>> edges;
    int cur = 0;
    for (int i = 0; i < n; ++i) {
        double a, b;
        std::string x, op, y;
        std::cin >> a >> x >> op >> b >> y;
        if (!mp.count(x)) mp[x] = cur++;
        if (!mp.count(y)) mp[y] = cur++;
        int u = mp[x], v = mp[y];
        edges.emplace_back(b, u, v);
        edges.emplace_back(a / b, v, u);
    }
    std::vector g(cur, std::vector<double>(cur, 1e305));
    std::vector f(cur, std::vector<int>(cur, 0)), cnt(cur, std::vector<int>(cur, 0));
    for (auto [c, a, b] : edges) {
        g[a][b] = c;
        g[a][a] = g[b][b] = 1;
        f[a][b] = f[a][a] = f[b][b] = 1;
    }
    for (int k = 0; k < cur; ++k) {
        for (int i = 0; i < cur; ++i) {
            for (int j = 0; j < cur; ++j) {
                f[i][j] |= f[i][k] & f[k][j];
                if (f[i][j] && !cnt[i][j]) {
                    cnt[i][j] += 1;
                    g[i][j] = std::min(g[i][k] * g[k][j], g[i][j]);
                }
            }
        }
    }
    while (m--) {
        double x;
        std::string a, op, b;
        std::cin >> x >> a >> op >> b;
        if (!mp.count(a) || !mp.count(b)) {
            std::cout << "impossible" << "\n";
        } else {
            int u = mp[a], v = mp[b];
            if (f[u][v]) {
                std::cout << g[u][v] * x << "\n";
            } else {
                std::cout << "impossible" << '\n';
            }
        }
    }
}

原題在這裡(╹▽╹)

J. Jagged Skyline (隨機化+二分)

分析

題目只給了12000次詢問,顯然如果每個點問過去次數顯然是不夠的。這邊可以做個隨機化問點,每次查詢當前最高值+1,如果為sky那就直接跳過,否則說明當前的位置比我最大值要大,此時更新一下最大值。期望的詢問次數大概是\(O(n + ln(w) * log_2(n))\)

程式碼實現

#include <bits/stdc++.h>
std::mt19937 rng(std::chrono::steady_clock::now().time_since_epoch().count());
int main() {
    std::cin.tie(nullptr)->sync_with_stdio(false);
    #define int long long
    int n, h, cur = 11999;
    std::cin >> n >> h;
    std::vector<int> ord(n);
    std::iota(ord.begin(), ord.end(), 1);
    std::shuffle(ord.begin(), ord.end(), std::default_random_engine(rng()));
    auto get = [&](int w, int high) {
        std::cout << "! " << w << ' ' << high << std::endl;
        exit(0);
    };
    auto ask = [&](int W, int H) {
        cur--;
        std::cout << "? " << W << " " << H << std::endl;
        std::string x;
        std::cin >> x;
        return x;
    };
    int max = 0, pos = 1;
    for (int i = 0; i < n; ++i) {
        if (ask(ord[i], max + 1) != "sky") {
            if (cur == 0) get(pos, max);
            if (ask(ord[i], h) == "building") get(ord[i], h);
            int l = max + 1, r = h;
            while (l < r) {
                int mid = l + r >> 1;
                if (ask(ord[i], mid) == "sky") {
                    r = mid;
                } else {
                    l = mid + 1;
                }
                if (cur == 0) get(pos, max);
            }
            if (l - 1 > max) {
                pos = ord[i], max = l - 1;
                if (max == h) get(pos, max);
            }
        }
    }
    get(pos, max);
}

原題在這裡(╹▽╹)

L. Lowest Latency (三維最近點對)

分析

這題不知道是水還是怎麼的,貼了一個二維最近點對的程式碼就創過去了。

程式碼實現

#include <bits/stdc++.h>

using namespace std;
using DB = long double;

const DB eps = 1e-12, inf = 1e100, pi = acos(-1);
DB dcmp(DB x, DB y) { return fabs(x - y) < eps ? 0 : x < y ? -1 : 1; }
DB sgn(DB x) { return fabs(x) < 0 ? 0 : x < 0 ? -1 : 1; }
DB rand_eps() { return ((DB)rand() / RAND_MAX - 0.5) * eps; }
struct Point3 {
    DB x, y, z;
    Point3 () {}
    Point3 (DB x, DB y, DB z) : x(x), y(y), z(z) {}
    void shake() { x += rand_eps(), y += rand_eps(), z += rand_eps(); }
    Point3 operator+(const Point3 &P) const { return Point3(x + P.x, y + P.y, z + P.z); }
    Point3 operator-(const Point3 &P) const { return Point3(x - P.x, y - P.y, z - P.z); }
    Point3 operator*(DB p) const { return Point3(x * p, y * p, z * p); }
    Point3 operator/(DB p) const { return Point3(x / p, y / p, z / p); }
    DB operator&(const Point3 &P) const { return x * P.x + y * P.y + z * P.z; }
    Point3 operator^(const Point3 &P) const { return Point3(y * P.z - z - P.y, z * P.x - x * P.z, x * P.y - y * P.x); }
    friend istream &operator>>(istream &is, Point3 &rhs) { return is >> rhs.x >> rhs.y >> rhs.z; }
    friend ostream &operator<<(ostream &os, const Point3 &rhs) { return os << '(' << rhs.x << ',' << rhs.y << ',' << rhs.z << ')'; }
};
using Vector3 = Point3;
DB Len(const Vector3 &A) { return sqrt(A & A); }
DB Len2(const Vector3 &A) { return A & A; }
DB Distance(const Vector3 &A, const Vector3 &B) { return Len(A - B); }
DB Closest_pair(const vector<Point3> &p, int l, int r) { // 平面最近點對
    DB dist = inf;
    if (l == r) return dist;
    if (l + 1 == r) return Distance(p[l], p[r]);
    int mid = l + r >> 1;
    DB d1 = Closest_pair(p, l, mid), d2 = Closest_pair(p, mid + 1, r);
    dist = min(d1, d2);
    vector<Point3> tmp;
    for (int i = l; i <= r; ++i)
        if (fabs(p[mid].x - p[i].x) <= dist) tmp.push_back(p[i]);
    for (int i = 0; i < tmp.size(); ++i) {
        for (int j = i + 1; j < tmp.size(); ++j) {
            // if (fabs(tmp[j].y - tmp[i].y) >= dist || fabs(tmp[j].z - tmp[i].z) >= dist) break;
            dist = min(dist, Distance(tmp[i], tmp[j]));
        }
    }
    return dist;
}
int main() {
    std::cin.tie(nullptr)->sync_with_stdio(false);
    std::cout << std::fixed << std::setprecision(20) << "\n";
    int n;
    std::cin >> n;
    std::vector<Point3> p(n);
    for (int i = 0; i < n; ++i) {
        std::cin >> p[i];
    }
    std::sort(p.begin(), p.end(), 
        [&](const Point3& a, const Point3& b) {
            if (a.x == b.x) {
                if (a.y == b.y) {
                    return a.z < b.z;
                } else {
                    return a.y < b.y;
                }
            } else {
                return a.x < b.x;
            }
        });
    std::cout << Closest_pair(p, 0, n - 1) << "\n";
}

原題在這裡(╹▽╹)

相關文章