跳转至

ABC375(A-G)

A - Seats

题目大意

给你一个字符串 \(S\)\(S\) 由字符 #. 组成,问: \(S\) 中有多少个 . 的两边是 #

参考代码
#include <iostream>
#include <string>

using namespace std;

int main()
{
    int n;
    string s;
    cin >> n >> s;
    int ans = 0;
    for(int i = 1; i < n - 1; i++)
    {
        if(s[i] == '.' && s[i-1] == '#' && s[i+1] == '#')
        {
            ans++;
            i++;
        }
    }
    cout << ans << endl;
    return 0;
}

B - Traveling Takahashi Problem

题目大意

你在二维平面的原点,平面上有 \(N\) 个点,第 \(i\) 个点的坐标是 \((X_i, Y_i)\),你将按顺序访问这 \(N\) 个点,问:总路程是多少?

参考代码
#include <cmath>
#include <iomanip>
#include <iostream>

using namespace std;

double distance(double x1, double y1, double x2, double y2) { return sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)); }

int main()
{
    int n;
    cin >> n;
    double x = 0, y = 0, nx, ny;
    double ans = 0;
    for (int i = 0; i < n; i++)
    {
        cin >> nx >> ny;
        ans += distance(x, y, nx, ny);
        x = nx;
        y = ny;
    }
    ans += distance(x, y, 0, 0);
    cout << fixed << setprecision(6) << ans << endl;
    return 0;
}

C - Spiral Rotation

题目大意

给你一个 \(N \times N\) 的网格,其中 \(2 \le N \le 3000\)\(N\) 是偶数。网格中每个格子有一个字符。

你将执行以下操作 \(\frac{N}{2}\) 次:在第 \(i(i = 1, 2, ..., \frac{N}{2})\) 次操作中,对于所有满足 \(i \le x, y \le N+1-i\)\(x\)\(y\)同时\((y, N+1-x)\) 的字符替换成 \((x, y)\) 的字符。

输出执行所有操作后的网格。

解题思路

可以发现一次操作相当于将 \(i \le x, y \le N+1-i\) 范围内字符顺时针旋转 \(90^\circ\),但是直接模拟的话时间复杂度达到 \(O(N^3)\)。进一步可以发现,从 \(i\)\(i+1\) 需要旋转的圈数越来越少,这样从最外圈开始,每一圈旋转的次数都可以算出来,模 \(4\) 即可。

参考代码
#include <iostream>
#include <string>
#include <vector>

using namespace std;

int n;

void print(const vector<string> &g)
{
    for (auto s : g)
        cout << s << endl;
    cout << endl;
}

void spin(vector<string> &g, int r)
{
    string t = g[r];
    for (int j = r; j < n - r; j++)
        g[r][j] = g[n - j - 1][r];
    for (int j = r; j < n - r; j++)
        g[j][r] = g[n - r - 1][j];
    for (int j = r; j < n - r; j++)
        g[n - r - 1][j] = g[n - j - 1][n - r - 1];
    for (int j = r; j < n - r; j++)
        g[j][n - r - 1] = t[j];
}

int main()
{
    cin >> n;
    vector<string> g(n);
    for (int i = 0; i < n; i++)
        cin >> g[i];
    for (int i = 0; i < n / 2; i++)
    {
        int cnt = (i + 1) % 4;
        while (cnt--)
            spin(g, i);
    }
    print(g);
    return 0;
}

D - ABA

题目大意

给你一个字符串 \(S(1 \le |S| \le 2 \times 10 ^ 5)\),字符串由大写字母构成。设 \(S_x\) 表示 \(S\) 的第 \(x\) 个字符。

找到满足以下条件的三元组 \((i, j, k)\) 的个数:

  • \(1 \le i \le j \le k \le |S|\)
  • \(S_iS_jS_k\) 是回文字符串
解题思路

第一种做法我们可以枚举 \(j\),然后维护左右两边每种字符的数量,直接用左边的字符数乘以右边的字符数就好,时间复杂度 \(O(26|S|)\)

第二种做法我们可以枚举 \(k\),维护之前出现的每种字符的数量以及坐标之和。遍历到 \(k\) 的时候,查找与 \(S_k\) 相等的字符有多少个,再算出中间能插入多少个字符,时间复杂度 \(O(|S|)\),下面的代码是第二种做法。

参考代码
#include <iostream>
#include <string>
#include <unordered_map>

using namespace std;
using LL = long long;

struct Node
{
    int cnt;
    LL sum;
};

int main()
{
    string s;
    cin >> s;
    int n = s.size();
    unordered_map<char, Node> mp;
    LL ans = 0;
    for(int i = 0; i < n; i++)
    {
        char ch = s[i];
        auto it = mp.find(ch);
        if(it == mp.end())
        {
            mp[ch] = {1, i};
            continue;
        }
        auto &[cnt, sum] = it->second;
        ans += (LL)cnt * (i - 1) - sum;
        cnt++;
        sum += i;
    }
    cout << ans << endl;
    return 0;
}

E - 3 Team Division

题目大意

\(N(3 \le N \le 100)\) 个人和 \(3\) 只队伍,初始的时候,第 \(i\) 个人属于 \(A_i(A_i \in \{1, 2, 3\})\) 队伍。第 \(i\) 个人的力量为 \(B_i\),其中 \(\sum\limits_{i=1}^N B_i \le 1500\),队伍的总力量值为队伍中所有人的力量的和。你可以将任意一个人换到任意队伍中,问:至少要更换多少个人的队伍,才能让三支队伍的总力量值都相等?如果无解输出 -1

解题思路

注意到 \(\sum\limits_{i=1}^N B_i \le 1500\),即 \(\frac{1}{3}\sum\limits_{i=1}^N B_i \le 500\),考虑 \(dp[i][x][y]\) 表示前 \(i\) 个人分配完毕后,第一支队伍的力量值之和为 \(x\),第二支队伍的力量值之和为 \(y\) 的所有方案中操作次数的最小值。转移的时候根据背包 \(dp\) 转移就好。

下面的代码我用了哈希表只存了有效的状态,实际没有必要,直接数组就好。

参考代码
#include <iostream>
#include <unordered_map>
#include <utility>
#include <vector>

using namespace std;

const int P = 501;

int make_key(int x, int y) {return y * P + x;}
pair<int, int> get_xy(int key) {return {key % P, key / P};}

void update(unordered_map<int, int> *mp, int x, int y, int cnt)
{
    int key = make_key(x, y);
    auto it = mp->find(key);
    if (it == mp->end())
        mp->insert({key, cnt});
    else
        it->second = min(it->second, cnt);
}

int main()
{
    int n, target = 0;
    cin >> n;
    vector<int> a(n), b(n);
    for (int i = 0; i < n; i++)
    {
        cin >> a[i] >> b[i];
        target += b[i];
    }
    if (target % 3 != 0)
    {
        cout << "-1" << endl;
        return 0;
    }
    target /= 3;
    int sum = 0;
    unordered_map<int, int> dp[2], *now = &dp[0], *pre = &dp[1];
    now->insert({0, 0});
    for (int i = 0; i < n; i++)
    {
        swap(now, pre);
        now->clear();
        sum += b[i];
        for (auto [key, cnt]: *pre)
        {
            const auto [x, y] = get_xy(key);
            if (x + b[i] <= target)
                update(now, x + b[i], y, cnt + (a[i] != 1));
            if (y + b[i] <= target)
                update(now, x, y + b[i], cnt + (a[i] != 2));
            if (sum - x - y <= target)
                update(now, x, y, cnt + (a[i] != 3));
        }
    }
    auto it = now->find(make_key(target, target));
    cout << (it == now->end() ? -1 : it->second);
    return 0;
}

F - Road Blocked

题目大意

初始的时候,给你一个 \(N(2 \le N \le 300)\) 个点, \(M(0 \le M \le \frac{N(N-1)}{2})\) 条边的无向有权图。

接下来,你需要处理 \(Q(2 \times 10 ^ 5)\) 个询问,每一种询问有两种类型:

  • 1 x 表示删除第 \(i\) 条边。
  • 2 x y 表示查询点 \(x\) 到点 \(y\) 的最短路长度。如果不可达输出 -1

保证第一种操作至多出现 \(300\) 次。

解题思路

考虑离线处理,先用至始至终没被删过的边跑一边 Floyed,然后从最后一个询问开始,对于第二种操作就能 \(O(1)\) 回答。

考虑第一种操作如何处理,删除边就相当于加边,加边之后需要更新最短路的矩阵,设 \(dis[u][v]\) 表示 \(u\)\(v\) 的最短路,边为 \((a, b)\),则再加边之后,\(u\)\(v\) 的最短路有以下情况:

  • 不走边 \((a, b)\),则仍然是 \(dis[u][v]\)
  • \(u \rightarrow a \rightarrow b \rightarrow v\),即 \(dis[u][a] + w(a, b) + dis[b][v]\)
  • \(u \rightarrow b \rightarrow a \rightarrow v\),即 \(dis[u][b] + w(a, b) + dis[a][v]\)

取最小值更新就好,这样每次更新的时间复杂度是 \(O(N^2)\),又因为第一种操作至多 \(300\) 次,所以能过。

参考代码
#include <algorithm>
#include <iostream>
#include <limits>
#include <vector>

using namespace std;
using LL = long long;

const LL INF = numeric_limits<LL>::max() / 4;

int main()
{
    int n, m, q;
    cin >> n >> m >> q;
    vector<int> a(m), b(m), c(m);
    vector<bool> del(m);
    for (int i = 0; i < m; i++)
        cin >> a[i] >> b[i] >> c[i];
    vector<LL> ans(q, -1);
    vector<vector<int>> querys(q);
    for (int i = 0; i < q; i++)
    {
        int op, x, y;
        cin >> op >> x;
        if (op == 1)
        {
            x--;
            querys[i] = {op, x};
            del[x] = true;
        }
        else
        {
            cin >> y;
            querys[i] = {op, x, y};
        }
    }
    vector<vector<LL>> dis(n + 1, vector<LL>(n + 1, INF));
    for (int u = 1; u <= n; u++)
        dis[u][u] = 0;
    for (int i = 0; i < m; i++)
        if (!del[i])
            dis[a[i]][b[i]] = dis[b[i]][a[i]] = c[i];
    for (int k = 1; k <= n; k++)
        for (int u = 1; u <= n; u++)
            for (int v = 1; v <= n; v++)
                dis[u][v] = min(dis[u][v], dis[u][k] + dis[k][v]);
    for (int i = q - 1; i >= 0; i--)
    {
        if (querys[i][0] == 2)
        {
            int x = querys[i][1], y = querys[i][2];
            ans[i] = dis[x][y] >= INF ? -1 : dis[x][y];
        }
        else
        {
            int idx = querys[i][1];
            for (int u = 1; u <= n; u++)
                for (int v = 1; v <= n; v++)
                    dis[u][v] = min({dis[u][v], dis[u][a[idx]] + c[idx] + dis[b[idx]][v], dis[u][b[idx]] + c[idx] + dis[a[idx]][v]});
        }
    }
    for (int i = 0; i < q; i++)
        if (querys[i][0] == 2)
            cout << ans[i] << '\n';
    return 0;
}

G - Road Blocked 2

题目大意

给你一个 \(N(2 \times 10 ^ 5)\) 个点,\(M(1 \le M \le 2 \times 10 ^ 5)\) 条边的无向有权图。

对于 \(i = 1, 2, ..., M\),判断下面两个值是否相等:

  • \(1\) 到 点 \(N\) 的最短路长度。

  • 在仅仅删掉第 \(i\) 条边的情况下,点 \(1\) 到点 \(N\) 的最短路长度。

如果这两个值相等输出 Yes,否则输出 No

解题思路

首先以点 \(1\) 为起点跑最短路,如果 \(1\)\(n\) 不可达,那么所有的答案都是 Yes

然后以点 \(n\) 为起点跑一遍最短路,这样可以得到两个数组 \(dis_1\)\(dis_n\),我们需要构成一个子图,子图的边是能够成为 \(1\)\(n\) 的最短路边,对于一条边 \((a, b)\),如果这条边是最短路,要么是 \(1 \rightarrow a \rightarrow b \rightarrow n\),要么是 \(1 \rightarrow b \rightarrow a \rightarrow n\),所以判别条件就是 \(dis_1[a] + w(a, b) + dis_n[b] = dis_1[n]\),或者 \(dis_1[b] + w(a, b) + dis_n[a] = dis_1[n]\)

构建完子图之后,在子图上跑 tarjan 找割边即可。如果一条边出现在子图的割边中,则一定删去这条边一定会导致最短路长度发生变化。

参考代码
#include <algorithm>
#include <functional>
#include <iostream>
#include <limits>
#include <queue>
#include <unordered_set>
#include <vector>

using namespace std;
using LL = long long;

const LL INF = numeric_limits<LL>::max() / 2;

struct Graph
{
    struct Edge
    {
        int to, w;
    };

    int n;
    vector<vector<Edge>> edge;

    Graph(int n) : n(n), edge(n + 1) {}

    void add_edge(int u, int v, int w)
    {
        edge[u].push_back({v, w});
        edge[v].push_back({u, w});
    }

    vector<LL> dijkstra(int s)
    {
        struct Node
        {
            int u;
            LL dis;
            bool operator>(const Node &other) const { return dis > other.dis; }
        };

        vector<LL> dis(n + 1, INF);
        dis[s] = 0;
        priority_queue<Node, vector<Node>, greater<Node>> heap;
        heap.push({s, 0});
        while (!heap.empty())
        {
            auto [u, d] = heap.top();
            heap.pop();
            if (d > dis[u])
                continue;
            for (auto e : edge[u])
            {
                int v = e.to;
                LL nd = d + e.w;
                if (nd < dis[v])
                {
                    dis[v] = nd;
                    heap.push({v, nd});
                }
            }
        }
        return dis;
    }
};

struct SubGraph
{
    struct Edge
    {
        int to, rev_idx;
        size_t edge_idx;
    };

    int n, ti = 1;
    vector<vector<Edge>> edge;
    vector<int> dfn, low;

    SubGraph(int n) : n(n), edge(n + 1), dfn(n + 1), low(n + 1) {}

    void add_edge(int u, int v, size_t idx)
    {
        int ui = edge[u].size();
        int vi = edge[v].size();
        edge[u].push_back({v, vi, idx});
        edge[v].push_back({u, ui, idx});
    }

    void tarjan(unordered_set<int> &bridge, int u, size_t rev_idx)
    {
        dfn[u] = low[u] = ti++;
        for (size_t i = 0; i < edge[u].size(); i++)
        {
            if (i == rev_idx)
                continue;
            Edge &e = edge[u][i];
            int v = e.to;
            if (!dfn[v])
            {
                tarjan(bridge, v, e.rev_idx);
                low[u] = min(low[u], low[v]);
            }
            else
                low[u] = min(low[u], dfn[v]);
            if (low[v] > dfn[u])
                bridge.insert(e.edge_idx);
        }
    }

    unordered_set<int> find_bridge()
    {
        unordered_set<int> bridge;
        tarjan(bridge, 1, -1);
        return bridge;
    }
};

int main()
{
    int n, m;
    cin >> n >> m;
    vector<int> a(m), b(m), c(m);
    Graph g(n);
    SubGraph sub(n);
    for (int i = 0; i < m; i++)
    {
        cin >> a[i] >> b[i] >> c[i];
        g.add_edge(a[i], b[i], c[i]);
    }
    vector<LL> dis1 = g.dijkstra(1);
    vector<LL> disn = g.dijkstra(n);
    for (int i = 0; i < m; i++)
    {
        int u = a[i], v = b[i], w = c[i];
        if (dis1[u] + w + disn[v] == dis1[n] || dis1[v] + w + disn[u] == dis1[n])
            sub.add_edge(u, v, i);
    }
    unordered_set<int> bridge = sub.find_bridge();
    for (int i = 0; i < m; i++)
        cout << (bridge.count(i) ? "Yes" : "No") << '\n';
    return 0;
}


最后更新: 2024-12-19
创建日期: 2024-12-19