2023 ICPC Hefei

2023 ICPC Hefei

J

对于一条路径,维护最大的边权是容易的,但是要求路径上最大和次大的和。于是我们就枚举一条边来作为路径上的最大边权,然后取这条边的两个端点到原点和终点的路径上的最大边权为次大值就好了。只需要从原点和终点分别跑一遍 dijkstra 求出到每个点的最短距离,这里的一条路径的长度定义为这条路径上最大的边权。

int n, m;
std::array<int, 3> e[M + 5];
std::vector<std::pair<int, int>> adj[N + 5];
int dis1[N + 5], disn[N + 5];
bool vis[N + 5];

void dij(int S, int *dis) {
    std::priority_queue<std::pair<int, int>> q;
    for (int i = 1; i <= n; ++i) {
        vis[i] = false;
    }

    dis[S] = 0;
    q.push({ 0, S });
    while (not q.empty()) {
        auto [d, cur] = q.top();
        q.pop();

        if (vis[cur]) {
            continue;
        }
        vis[cur] = true;
        d = -d;

        for (auto &[to, w] : adj[cur]) {
            if (dis[to] > std::max(d, w)) {
                dis[to] = std::max(d, w);
                q.push({ -dis[to], to });
            }
        }
    }
    return;
}

void solve() {
    std::cin >> n >> m;
    for (int i = 0; i < m; ++i) {
        int u = 0, v = 0, w = 0;
        std::cin >> u >> v >> w;
        e[i] = { u, v, w };

        adj[u].push_back({ v, w });
        adj[v].push_back({ u, w });
    }

    for (int i = 1; i <= n; ++i) {
        dis1[i] = disn[i] = Inf;
    }

    dij(1, dis1);
    dij(n, disn);

    int ans = Inf;
    // 枚举路径上的最长边
    for (int i = 0; i < m; ++i) {
        auto &[u, v, w] = e[i];

        int w1 = std::max(dis1[u], disn[v]);
        int w2 = std::max(dis1[v], disn[u]);
        // 找次长边
        int mn = std::min(w1, w2);

        if (w >= mn) {
            ans = std::min(ans, w + mn);
        }
    }
    std::cout << ans << '\n';
}

C

我是字符串苦手,竟然是回文自动机板子题吗。

回文自动机的每个节点就是一个本质不同的回文子串,每个节点可以记录下对应串的长度信息和数量信息。对于一个特定回文串的数量,需要建立一颗 fail 树,一个节点对应的串的数量就是以这个节点为根的子树里所有节点记录的数量和。

B

首先要观察出合法的子串满足最长递减子序列的长度小于等于 2。这个东西和可以直接 dp 的。从大到小枚举每个数,假设已经放好了比 \(k\) 大的所有数,现在要考虑 \(k\) 的位置,首先把 \(k\) 全部放到第一个数的前面是可以的,因为现在的 \(k\) 是不大于枚举到的所有数的,放到最前面一定不会使最长递减子序列的长度增加,但是如果放在第一个数后面就有可能,于是我们需要知道当前序列中,第一个使得最长递减子序列的长度是 2 的位置,于是所有的 \(k\) 必须都放在这个位置之前,知道了这个后,枚举一下有多少 \(k\) 在第一个数之前 和 在第一个数之后的 \(k\) 的第一个位置在哪里,算一下排列组合就好。为了方便,我们从逆序列考虑,找出最长递增子串长度小于等于2的逆序列的数量,于是我们可以定义 \(f_{i, j}\) 为考虑了前 \(i\) 个数,且第一个使得最长递增子序列的长度为 2 的位置在 \(j\),可以得到以下转移:

\[f_{i + 1, x + k + 1} = f_{i + 1, x + k + 1} + f[i][j] \times C_{(j - k - 1) + (a_{i + 1} - x - 1)}^{a_{i + 1} - x - 1} \]

posted @ 2025-10-16 23:19  Young_Cloud  阅读(14)  评论(0)    收藏  举报