zoukankan      html  css  js  c++  java
  • 牛客小白月赛27

    比赛链接:https://ac.nowcoder.com/acm/contest/6874#question

    A - 巨木之森

    题解

    除了起点到终点的路径,其他的路径都会走两遍,所以为了尽可能减少折返的路径,每次应使起点终点所在路径尽可能的长,亦即二者距离尽可能的远。

    最长的一条路径为树的直径,根据树的直径的性质,任意一点的最远点一定为树的直径的两个端点之一。

    为了寻找树的直径的两个端点A、B,可以从任意一点BFS得到树的直径的一端点A,再以端点A为起点BFS得到直径的另一端点B。

    得到端点A、B后,BFS得到每个点距离A、B的距离,从每个点 $i$ 出发遍历全树的花费即为:

    $2 imes$ 所有路径长 $- max(dis_{Ai},dis_{Bi})$

    对所有点的花费排序,从小到大采用即可。

    代码

    #include <bits/stdc++.h>
    #define int long long
    using namespace std;
    signed main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
        int n, m;
        cin >> n >> m;
        vector<vector<pair<int, int>>> G(n);
        int tot = 0;
        for (int i = 0; i < n - 1; i++) {
            int u, v, w;
            cin >> u >> v >> w;
            --u, --v;
            tot += w;
            G[u].emplace_back(v, w);
            G[v].emplace_back(u, w);
        }
        vector<int> disA(n), disB(n);
        function<void(int, vector<int> &)> bfs = [&](int s, vector<int> &dist) {
            vector<bool> vis(n);
            queue<pair<int, int>> que;
            que.emplace(s, 0);
            vis[s] = true;
            while (!que.empty()) {
                int u = que.front().first;
                int dis = que.front().second;
                que.pop();
                dist[u] = dis;
                for (auto i : G[u]) {
                    int v = i.first;
                    int w = i.second;
                    if (!vis[v]) {
                        que.emplace(v, dis + w);
                        vis[v] = true;
                    }
                }
            }
        };
        bfs(0, disA);
        int A = max_element(disA.begin(), disA.end()) - disA.begin();
        bfs(A, disA);
        int B = max_element(disA.begin(), disA.end()) - disA.begin();
        bfs(B, disB);
        vector<int> wt(n);
        for (int i = 0; i < n; i++) {
            wt[i] = tot * 2 - max(disA[i], disB[i]);
        }
        sort(wt.begin(), wt.end());
        int ans = 0;
        for (int i = 0; i < n; i++) {
            m -= wt[i];
            if (m >= 0) {
                ++ans;
            } else {
                break;
            }
        }
        cout << ans << "
    ";
        return 0;
    }

    B - 乐**对

    题解

    如果每个乐手都可以被分进一队,那么一定有 $max_{a_i} le n$ 。

    将 $a_i$ 排序,为了保证能力值最大的乐手可以被分进一队,先将他及之前共 $a_n$ 个人分为一队。

    之后对于前面的 $n - a_n$ 人进行 $dp$,每个人有两种选择:

    • $dp_i = dp_{i - 1}$,加入之前的队伍
    • $dp_i = dp_{i - a_i} + 1$,与前面的 $a_i$ 人另成一队

    最终答案即为 $dp_{n - a_n} + 1$ 。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
        int n;
        cin >> n;
        vector<int> a(n + 1);
        for (int i = 1; i <= n; i++)
            cin >> a[i];
        sort(a.begin(), a.end());
        if (a[n] > n) {
            cout << -1 << "
    ";
            return 0;
        }
        vector<int> dp(n + 1);
        for (int i = 1; i <= n - a[n]; i++) {
            int j = i - a[i];
            dp[i] = max(dp[i - 1], (j >= 0 ? dp[j] + 1 : 0));
        }
        cout << dp[n - a[n]] + 1 << "
    ";
        return 0;
    }

    C - 光玉小镇

    题解

    因为 T 的数目最多为 15,所以如果枚举所有可能的修理路径,T! 是会超时的,这个时候可以考虑状压 $dp$ 。

    首先 BFS 得到每个电线杆离家的距离 $disS$,以及电线杆两两之间的距离 $dis_{ij}$,同时初始化 $dp$ 数组:

    for (int i = 0; i < int(T.size()); i++) {
        dp[1 << i][i] = disS[i] = bfs(Sx, Sy, T[i].first, T[i].second);
    }

    将每个电线杆的修理状态表示为 $dp_{ij}$ 中 $i$ 的一个比特,$j$ 表示为当前状态下最后修的是哪一个电线杆,状态转移方程即为:

    for (int i = 0; i < (1 << T.size()); i++) {
        for (int j = 0; j < int(T.size()); j++) {
            if (((1 << j) & i) == 0) continue;
            for (int k = 0; k < int(T.size()); k++) {
                if ((1 << k) & i) continue;
                dp[i | (1 << k)][k] = min(dp[i | (1 << k)][k], dp[i][j] + dis[j][k]);
            }
        }
    }

    $ans$ 即为:

    for (int i = 0; i < int(T.size()); i++) {
        ans = min(ans, dp[(1 << T.size()) - 1][i] + disS[i] + 1LL * int(T.size()) * t);
    }

    代码

    #include <bits/stdc++.h>
    using namespace std;
    const int dir[4][2] = {{0, -1}, {0, 1}, {-1, 0}, {1, 0}};
    struct P{
        int x, y, dis;
    };
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
        int n, m, t;
        cin >> n >> m >> t;
        vector<vector<char>> MP(n, vector<char> (m));
        int Sx = -1, Sy = -1;
        vector<pair<int, int>> T;
        for (int i = 0; i < n; i++)
            for (int j = 0; j < m; j++) {
                cin >> MP[i][j];
                if (MP[i][j] == 'S') {
                    Sx = i, Sy = j;
                }
                if (MP[i][j] == 'T') {
                    T.emplace_back(i, j);
                }
            }
        function<bool(int, int)> legal = [&](int x, int y) {
            return 0 <= x and x < n and 0 <= y and y < m and MP[x][y] != '#';
        };
        function<int(int, int, int, int)> bfs = [&](int sx, int sy, int ex, int ey) {
            vector<vector<bool>> vis(n, vector<bool> (m));
            queue<P> que;
            que.push({sx, sy, 0});
            vis[sx][sy] = true;
            while (!que.empty()) {
                int x = que.front().x;
                int y = que.front().y;
                int dis = que.front().dis;
                que.pop();
                if (x == ex and y == ey) {
                    return dis;
                }
                for (int i = 0; i < 4; i++) {
                    int nx = x + dir[i][0];
                    int ny = y + dir[i][1];
                    if (legal(nx, ny) and !vis[nx][ny]) {
                        que.push({nx, ny, dis + 1});
                        vis[nx][ny] = true;
                    }
                }
            }
            return -1;
        };
        vector<vector<int>> dp(1 << T.size(), vector<int> (T.size(), 1e9));
        vector<int> disS(T.size());
        for (int i = 0; i < int(T.size()); i++) {
            dp[1 << i][i] = disS[i] = bfs(Sx, Sy, T[i].first, T[i].second);
            if (disS[i] == -1) {
                cout << -1 << "
    ";
                return 0;
            }
        }
        vector<vector<int>> dis(T.size(), vector<int> (T.size()));
        for (int i = 0; i < int(T.size()); i++) {
            for (int j = i + 1; j < int(T.size()); j++) {
                dis[i][j] = dis[j][i] = bfs(T[i].first, T[i].second, T[j].first, T[j].second);
                if (dis[i][j] == -1) {
                    cout << -1 << "
    ";
                    return 0;
                }
            }
        }
        for (int i = 0; i < (1 << T.size()); i++) {
            for (int j = 0; j < int(T.size()); j++) {
                if (((1 << j) & i) == 0) continue;
                for (int k = 0; k < int(T.size()); k++) {
                    if ((1 << k) & i) continue;
                    dp[i | (1 << k)][k] = min(dp[i | (1 << k)][k], dp[i][j] + dis[j][k]);
                }
            }
        }
        long long ans = LLONG_MAX;
        for (int i = 0; i < int(T.size()); i++) {
            ans = min(ans, dp[(1 << T.size()) - 1][i] + disS[i] + 1LL * int(T.size()) * t);
        }
        cout << ans << "
    ";
        return 0;
    }

    D - 巅峰对决

    题解

    线段树维护区间最值,如果某个区间的值连续,那么该区间的最值差一定等于区间长度。

    代码

    #include <bits/stdc++.h>
    #define lson i << 1, l, mid
    #define rson i << 1 | 1, mid + 1, r
    #define mid ((l + r) >> 1)
    using namespace std;
    constexpr int N = 1e5 + 100;
    int mi[N << 2], mx[N << 2], a[N];
    void pushup(int i) {
        mi[i] = min(mi[i << 1], mi[i << 1 | 1]);
        mx[i] = max(mx[i << 1], mx[i << 1 | 1]);
    }
    void build(int i, int l, int r) {
        if (l == r) {
            mi[i] = mx[i] = a[l];
            return;
        }
        build(lson);
        build(rson);
        pushup(i);
    }
    void update(int i, int l, int r, int x, int p) {
        if (l == r) {
            mi[i] = mx[i] = p;
            return;
        }
        if (x <= mid) {
            update(lson, x, p);
        } else {
            update(rson, x, p);
        }
        pushup(i);
    }
    pair<int, int> query(int i, int l, int r, int L, int R) {
        if (r < L or l > R) return make_pair(INT_MAX, INT_MIN);
        if (L <= l and r <= R) return make_pair(mi[i], mx[i]);
        const pair<int, int> a = query(lson, L, R);
        const pair<int, int> b = query(rson, L, R);
        return make_pair(min(a.first, b.first), max(a.second, b.second));
    }
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
        int n, q;
        cin >> n >> q;
        for (int i = 1; i <= n; i++)
            cin >> a[i];
        build(1, 1, n);
        for (int i = 0; i < q; i++) {
            int op, l, r;
            cin >> op >> l >> r;
            if (op == 1) {
                update(1, 1, n, l, r);
            } else {
                const pair<int, int> q = query(1, 1, n, l, r);
                cout << (q.second - q.first == r - l ? "YES" : "NO") << "
    ";
            }
        }
        return 0;
    }

    E - 使徒袭来

    题解

    基本不等式:

    $frac{a_1 + a_2 + dots + a_n}{n} ge sqrt[n]{a_1 imes a_2 imes dots imes a_n}$

    本题为:

    $a_1 + a_2 + a_3 ge 3 imes sqrt[3]{a_1 imes a_2 imes a_3}$

    代码

    #include <bits/stdc++.h>
    using namespace std;
    int main() {
        int n;
        cin >> n;
        printf("%.3f
    ", 3.0 * pow(n, 1.0 / 3.0));
        return 0;
    }

    F - 核弹剑仙

    题解一

    根据武器的破坏力由弱到强单向建图,枚举出发点,被访问的点的个数即为破坏力强于出发点的武器个数。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
        int n, m;
        cin >> n >> m;
        vector<vector<int>> G(n);
        for (int i = 0; i < m; i++) {
            int u, v;
            cin >> u >> v;
            --u, --v;
            G[v].push_back(u);
        }
        vector<bool> vis(n);
        function<void(int)> dfs = [&](int u) {
            if (vis[u]) return;
            vis[u] = true;
            for (auto v : G[u]) {
                dfs(v);
            }
        };
        for (int i = 0; i < n; i++) {
            fill(vis.begin(), vis.end(), false);
            dfs(i);
            cout << count(vis.begin(), vis.end(), true) - 1 << "
    ";
        }
        return 0;
    }

    题解二

    根据武器的破坏力由强到弱单向建图拓扑排序,并用 $bitset$ 的状态来记录每个之前遍历到的武器,比当前武器破坏力强的武器个数即为  dp[i].count()  。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
        int n, m;
        cin >> n >> m;
        vector<vector<int>> G(n);
        vector<int> deg(n);
        for (int i = 0; i < m; i++) {
            int u, v;
            cin >> u >> v;
            --u, --v;
            G[u].push_back(v);
            ++deg[v];
        }
        vector<bitset<1005>> dp(n);
        queue<int> que;
        for (int i = 0; i < n; i++) {
            if (deg[i] == 0)
                que.push(i);
        }
        while (!que.empty()) {
            int u = que.front();
            que.pop();
            for (auto v : G[u]) {
                dp[v] |= dp[u];
                dp[v].set(u);
                if (--deg[v] == 0)
                    que.push(v);
            }
        }
        for (int i = 0; i < n; i++) 
            cout << dp[i].count() << " 
    "[i == n - 1];
        return 0;
    }

    G - 虚空之力

    题解一

    模拟。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
        int n;
        cin >> n;
        int a = 0, b = 0, c = 0, d = 0;
        for (int i = 0; i < n; i++) {
            char x;
            cin >> x;
            if (x == 'k') ++a;
            if (x == 'i') ++b;
            if (x == 'n') ++c;
            if (x == 'g') ++d;
        }
        int ans = 0;
        while (a >= 1 and b >= 2 and c >= 2 and d >= 2) {
            a -= 1;
            b -= 2;
            c -= 2;
            d -= 2;
            ans += 2;
        }
        while (a >= 1 and b >= 1 and c >= 1 and d >= 1) {
            a -= 1;;
            b -= 1;
            c -= 1;
            d -= 1;
            ans += 1;
        }
        cout << ans << "
    ";
        return 0;
    }

    题解二

    数学。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
        int n;
        cin >> n;
        int a = 0, b = 0, c = 0, d = 0;
        for (int i = 0; i < n; i++) {
            char x;
            cin >> x;
            if (x == 'k') ++a;
            if (x == 'i') ++b;
            if (x == 'n') ++c;
            if (x == 'g') ++d;
        }
        int mi = min({a, b / 2, c / 2, d / 2});
        cout << 2 * mi + min({a - mi, b - 2 * mi, c - 2 * mi, d - 2 * mi}) << "
    ";
        return 0;
    }

    H - 社*游戏

    题解

    二维前缀和 + 二分。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
        int n, m, k;
        cin >> n >> m >> k;
        vector<vector<char>> MP(n + 1, vector<char>(m + 1));
        for (int i = 1; i <= n; i++)
            for (int j = 1; j <= m; j++)
                cin >> MP[i][j];
        vector<vector<vector<int>>> dp(n + 1, vector<vector<int>>(m + 1, vector<int>(26)));
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                for (int k = 0; k < 26; k++) {
                    dp[i][j][k] = dp[i - 1][j][k] + dp[i][j - 1][k] - dp[i - 1][j - 1][k] + (MP[i][j] == 'a' + k);
                }
            }
        }
        function<bool(int, int, int)> ok = [&](int x, int y, int d) {
            int nx = x + d - 1, ny = y + d - 1;
            for (int i = 0; i < 26; i++) {
                if (dp[nx][ny][i] - dp[x - 1][ny][i] - dp[nx][y - 1][i] + dp[x - 1][y - 1][i] > k) {
                    return false;
                }
            }
            return true;
        };
        for (int i = 1; i <= n; i++) {
            for (int j = 1; j <= m; j++) {
                int l = 1, r = min(n - i + 1, m - j + 1);
                while (r - l > 1) {
                    int mid = (l + r) / 2;
                    if (ok(i, j, mid)) {
                        l = mid;
                    } else {
                        r = mid - 1;
                    }
                }
                cout << (ok(i, j, r) ? r : l) << " 
    "[j == m];
            }
        }
        return 0;
    }

    I - 名作之壁

    题解

    尺取 + 单调队列。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    constexpr int MOD = 1e9;
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
        int n, k, x, b, c;
        cin >> n >> k >> x >> b >> c;
        vector<int> a(n + 1);
        a[0] = x;
        long long ans = 0;
        deque<int> mi, mx;
        for (int l = 1, r = 1; r <= n; r++) {
            a[r] = (1LL * a[r - 1] * b + c) % MOD;
            while (mi.size() and a[mi.back()] >= a[r]) mi.pop_back();
            mi.push_back(r);
            while (mx.size() and a[mx.back()] <= a[r]) mx.pop_back();
            mx.push_back(r);
            while (a[mx.front()] - a[mi.front()] > k) {
                ans += n - r + 1;
                ++l;
                if (mi.front() < l) mi.pop_front();
                if (mx.front() < l) mx.pop_front();
            }
        }
        cout << ans << "
    ";
        return 0;
    }

    J - 逃跑路线

    题解

    $(2^1 - 1)  &   (2^2 - 1)  &   dots   &   (2^n - 1) = 1$

    即判断最后横坐标的奇偶性。

    代码

    #include <bits/stdc++.h>
    using namespace std;
    int main() {
        ios::sync_with_stdio(false);
        cin.tie(nullptr);
        int n;
        cin >> n;
        int ans = 0;
        for (int i = 0; i < n; i++) {
            string s;
            cin >> s;
            ans += (s.back() - '0') & 1;
        }
        cout << (ans & 1) << "
    ";
        return 0;
    }
  • 相关阅读:
    如何评测一个软件工程师的计算机网络知识水平与网络编程技能水平?
    如何评测软件工程知识技能水平
    深入理解TCP协议及其源代码
    Socket与系统调用深度分析
    创新产品的需求分析:未来的图书会是什么样子?
    构建调试Linux内核网络代码的环境MenuOS系统
    Java实现简单网络聊天程序
    适配器模式的探究
    Linux下ss命令的研究
    业务领域建模Domain Modeling
  • 原文地址:https://www.cnblogs.com/Kanoon/p/13718181.html
Copyright © 2011-2022 走看看