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;
    }
  • 相关阅读:
    msql 触发器
    微信模板消息推送
    微信朋友朋友圈自定义分享内容
    微信退款
    异步调起微信支付
    微信支付
    第一次作业
    【Linus安装MongoDB及Navicat】
    【前端】ES6总结
    【开发工具】Pycharm使用
  • 原文地址:https://www.cnblogs.com/Kanoon/p/13718181.html
Copyright © 2011-2022 走看看